Compare commits

..

9 Commits

Author SHA1 Message Date
Mads Marquart
1850131b97 Document that multiple windows are not supported on Android 2024-02-20 15:32:08 +01:00
Mads Marquart
2bc1943188 Pin ahash in CI (#3498)
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2024-02-16 17:51:48 +01:00
Kirill Chibisov
385c4b3c88 On Wayland, update title from AboutToWait
Fixes #3472.
2024-02-14 00:48:52 +04:00
Kirill Chibisov
ea70f773d3 On X11, don't require XIM to be present
In general, we may want to use xinput v2 for keyboard input in such
cases, so we have compose going, but for now just don't crash if
there's no XIM.
2024-02-13 07:49:58 +04:00
AryaveerSR
83012f4c1c Fix Documentation for ScaleFactorChanged in WindowEvent. (#3489) 2024-02-12 18:20:03 +01:00
Jeremy Soller
6825fed073 On Orbital, implement various Window methods
Implement the following methods on the `Window`:
  - `Window::set_cursor_grab`.
  - `Window::set_cursor_visible`.
  - `Window::drag_window`.
  - `Window::drag_resize_window`.
  - `Window::set_transparent`.
  - `Window::set_visible`.
  - `Window::is_visible`.
  - `Window::set_resizable`.
  - `Window::is_resizable`.
  - `Window::set_maximized`.
  - `Window::is_maximized`.
  - `Window::set_decorations`.
  - `Window::is_decorated`.
  - `Window::set_window_level`.

To make locked pointer useful, the `DeviceEvent::MouseMotion`
event was also implemented.
2024-02-11 04:40:06 +04:00
Kirill Chibisov
273984a385 On X11, extract event handlers
Make code more clear wrt explicit returns during event handling,
which may lead to skipped IME event handling.
2024-02-11 03:31:47 +04:00
Kirill Chibisov
dbe0f852da On X11, store window target on EventProcessor
Remove the redundant `Rc` to access the window target.
2024-02-11 03:31:47 +04:00
Kirill Chibisov
d1902aa15a On X11, don't require XSETTINGS
We could fail to setup property watcher and fail to start, thus
don't require XSETTINGS to work.

Fixes: df8805c0 (On X11, reload DPI on _XSETTINGS_SETTINGS)
2024-02-10 00:24:03 +04:00
18 changed files with 1899 additions and 1607 deletions

View File

@@ -89,7 +89,7 @@ jobs:
- name: Generate lockfile - name: Generate lockfile
# Also updates the crates.io index # Also updates the crates.io index
run: cargo generate-lockfile run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7
- name: Install GCC Multilib - name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686') if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')

View File

@@ -11,7 +11,7 @@ Unreleased` header.
# Unreleased # Unreleased
- **Breaking:** Use `RequestIgnored` as the error type in `InnerSizeWriter::request_inner_size`. - On X11, don't require XIM to run.
- Fix compatibility with 32-bit platforms without 64-bit atomics. - Fix compatibility with 32-bit platforms without 64-bit atomics.
- On X11, fix swapped instance and general class names. - On X11, fix swapped instance and general class names.
- **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`. - **Breaking:** Removed unnecessary generic parameter `T` from `EventLoopWindowTarget`.
@@ -54,6 +54,22 @@ Unreleased` header.
- On Orbital, fix `logical_key` and `text` not reported in `KeyEvent`. - On Orbital, fix `logical_key` and `text` not reported in `KeyEvent`.
- On Orbital, implement `KeyEventExtModifierSupplement`. - On Orbital, implement `KeyEventExtModifierSupplement`.
- On Orbital, map keys to `NamedKey` when possible. - On Orbital, map keys to `NamedKey` when possible.
- On Orbital, implement `set_cursor_grab`.
- On Orbital, implement `set_cursor_visible`.
- On Orbital, implement `drag_window`.
- On Orbital, implement `drag_resize_window`.
- On Orbital, implement `set_transparent`.
- On Orbital, implement `set_visible`.
- On Orbital, implement `is_visible`.
- On Orbital, implement `set_resizable`.
- On Orbital, implement `is_resizable`.
- On Orbital, implement `set_maximized`.
- On Orbital, implement `is_maximized`.
- On Orbital, implement `set_decorations`.
- On Orbital, implement `is_decorated`.
- On Orbital, implement `set_window_level`.
- On Orbital, emit `DeviceEvent::MouseMotion`.
- On Wayland, fix title in CSD not updated from `AboutToWait`.
# 0.29.10 # 0.29.10

View File

@@ -32,8 +32,6 @@
//! //!
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run //! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil //! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::error::Error;
use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
@@ -45,6 +43,7 @@ use smol_str::SmolStr;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::Instant; use web_time::Instant;
use crate::error::ExternalError;
#[cfg(doc)] #[cfg(doc)]
use crate::window::Window; use crate::window::Window;
use crate::{ use crate::{
@@ -537,9 +536,8 @@ pub enum WindowEvent {
/// * Changing the display's scale factor (e.g. in Control Panel on Windows). /// * Changing the display's scale factor (e.g. in Control Panel on Windows).
/// * Moving the window to a display with a different scale factor. /// * Moving the window to a display with a different scale factor.
/// ///
/// After this event callback has been processed, the window will be resized to whatever value /// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is
/// is pointed to by the `new_inner_size` reference. By default, this will contain the size suggested /// resized to the value suggested by the OS, but it can be changed to any value.
/// by the OS, but it can be changed to any value.
/// ///
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module. /// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
ScaleFactorChanged { ScaleFactorChanged {
@@ -1127,23 +1125,16 @@ impl InnerSizeWriter {
Self { new_inner_size } Self { new_inner_size }
} }
/// Try to request a new inner size which will be set synchronously on the /// Try to request inner size which will be set synchroniously on the window.
/// window.
///
///
/// # Errors
///
/// This method returns an error when the request was ignored because it
/// was done asynchronously, outside the event loop callback.
pub fn request_inner_size( pub fn request_inner_size(
&mut self, &mut self,
new_inner_size: PhysicalSize<u32>, new_inner_size: PhysicalSize<u32>,
) -> Result<(), RequestIgnored> { ) -> Result<(), ExternalError> {
if let Some(inner) = self.new_inner_size.upgrade() { if let Some(inner) = self.new_inner_size.upgrade() {
*inner.lock().unwrap() = new_inner_size; *inner.lock().unwrap() = new_inner_size;
Ok(()) Ok(())
} else { } else {
Err(RequestIgnored { _priv: () }) Err(ExternalError::Ignored)
} }
} }
} }
@@ -1154,22 +1145,6 @@ impl PartialEq for InnerSizeWriter {
} }
} }
/// The request to change the inner size synchronously was ignored.
///
/// See [`InnerSizeWriter::request_inner_size`] for details.
#[derive(Debug, Clone)] // Explicitly not other traits, in case we want to extend it in the future
pub struct RequestIgnored {
_priv: (),
}
impl fmt::Display for RequestIgnored {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("the request to change the inner size was ignored")
}
}
impl Error for RequestIgnored {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::event; use crate::event;

View File

@@ -498,7 +498,7 @@ impl<'a> KeyEventResults<'a> {
} }
} }
fn physical_key(&mut self) -> PhysicalKey { fn physical_key(&self) -> PhysicalKey {
keymap::raw_keycode_to_physicalkey(self.keycode) keymap::raw_keycode_to_physicalkey(self.keycode)
} }
@@ -553,6 +553,7 @@ impl<'a> KeyEventResults<'a> {
} else { } else {
0 0
}; };
self.keysym_to_key(keysym) self.keysym_to_key(keysym)
.unwrap_or_else(|(key, location)| { .unwrap_or_else(|(key, location)| {
( (
@@ -565,7 +566,7 @@ impl<'a> KeyEventResults<'a> {
}) })
} }
fn keysym_to_key(&mut self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> { fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
let location = super::keymap::keysym_location(keysym); let location = super::keymap::keysym_location(keysym);
let key = super::keymap::keysym_to_key(keysym); let key = super::keymap::keysym_to_key(keysym);
if matches!(key, Key::Unidentified(_)) { if matches!(key, Key::Unidentified(_)) {

View File

@@ -940,10 +940,12 @@ impl EventLoopWindowTarget {
} }
} }
#[allow(dead_code)]
fn set_exit_code(&self, code: i32) { fn set_exit_code(&self, code: i32) {
x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code)) x11_or_wayland!(match self; Self(evlp) => evlp.set_exit_code(code))
} }
#[allow(dead_code)]
fn exit_code(&self) -> Option<i32> { fn exit_code(&self) -> Option<i32> {
x11_or_wayland!(match self; Self(evlp) => evlp.exit_code()) x11_or_wayland!(match self; Self(evlp) => evlp.exit_code())
} }

View File

@@ -461,19 +461,19 @@ impl<T: 'static> EventLoop<T> {
window_ids.extend(state.window_requests.get_mut().keys()); window_ids.extend(state.window_requests.get_mut().keys());
}); });
for window_id in window_ids.drain(..) { for window_id in window_ids.iter() {
let event = self.with_state(|state| { let event = self.with_state(|state| {
let window_requests = state.window_requests.get_mut(); let window_requests = state.window_requests.get_mut();
if window_requests.get(&window_id).unwrap().take_closed() { if window_requests.get(window_id).unwrap().take_closed() {
mem::drop(window_requests.remove(&window_id)); mem::drop(window_requests.remove(window_id));
mem::drop(state.windows.get_mut().remove(&window_id)); mem::drop(state.windows.get_mut().remove(window_id));
return Some(WindowEvent::Destroyed); return Some(WindowEvent::Destroyed);
} }
let mut window = state let mut window = state
.windows .windows
.get_mut() .get_mut()
.get_mut(&window_id) .get_mut(window_id)
.unwrap() .unwrap()
.lock() .lock()
.unwrap(); .unwrap();
@@ -485,7 +485,7 @@ impl<T: 'static> EventLoop<T> {
// Reset the frame callbacks state. // Reset the frame callbacks state.
window.frame_callback_reset(); window.frame_callback_reset();
let mut redraw_requested = window_requests let mut redraw_requested = window_requests
.get(&window_id) .get(window_id)
.unwrap() .unwrap()
.take_redraw_requested(); .take_redraw_requested();
@@ -498,7 +498,7 @@ impl<T: 'static> EventLoop<T> {
if let Some(event) = event { if let Some(event) = event {
callback( callback(
Event::WindowEvent { Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(*window_id),
event, event,
}, },
&self.window_target, &self.window_target,
@@ -514,6 +514,42 @@ impl<T: 'static> EventLoop<T> {
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
callback(Event::AboutToWait, &self.window_target); callback(Event::AboutToWait, &self.window_target);
// Update the window frames and schedule redraws.
let mut wake_up = false;
for window_id in window_ids.drain(..) {
wake_up |= self.with_state(|state| match state.windows.get_mut().get_mut(&window_id) {
Some(window) => {
let refresh = window.lock().unwrap().refresh_frame();
if refresh {
state
.window_requests
.get_mut()
.get_mut(&window_id)
.unwrap()
.redraw_requested
.store(true, Ordering::Relaxed);
}
refresh
}
None => false,
});
}
// Wakeup event loop if needed.
//
// If the user draws from the `AboutToWait` this is likely not required, however
// we can't do much about it.
if wake_up {
match &self.window_target.p {
PlatformEventLoopWindowTarget::Wayland(window_target) => {
window_target.event_loop_awakener.ping();
}
#[cfg(x11_platform)]
PlatformEventLoopWindowTarget::X(_) => unreachable!(),
}
}
std::mem::swap(&mut self.compositor_updates, &mut compositor_updates); std::mem::swap(&mut self.compositor_updates, &mut compositor_updates);
std::mem::swap(&mut self.buffer_sink, &mut buffer_sink); std::mem::swap(&mut self.buffer_sink, &mut buffer_sink);
std::mem::swap(&mut self.window_ids, &mut window_ids); std::mem::swap(&mut self.window_ids, &mut window_ids);

View File

@@ -6,7 +6,7 @@ macro_rules! atom_manager {
($($name:ident $(:$lit:literal)?),*) => { ($($name:ident $(:$lit:literal)?),*) => {
x11rb::atom_manager! { x11rb::atom_manager! {
/// The atoms used by `winit` /// The atoms used by `winit`
pub(crate) Atoms: AtomsCookie { pub Atoms: AtomsCookie {
$($name $(:$lit)?,)* $($name $(:$lit)?,)*
} }
} }
@@ -14,7 +14,7 @@ macro_rules! atom_manager {
/// Indices into the `Atoms` struct. /// Indices into the `Atoms` struct.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub(crate) enum AtomName { pub enum AtomName {
$($name,)* $($name,)*
} }

View File

@@ -41,7 +41,7 @@ impl From<io::Error> for DndDataParseError {
} }
} }
pub(crate) struct Dnd { pub struct Dnd {
xconn: Arc<XConnection>, xconn: Arc<XConnection>,
// Populated by XdndEnter event handler // Populated by XdndEnter event handler
pub version: Option<c_long>, pub version: Option<c_long>,

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +1,33 @@
#![cfg(x11_platform)] #![cfg(x11_platform)]
mod activation; use std::cell::{Cell, RefCell};
mod atoms; use std::collections::{HashMap, HashSet};
mod dnd; use std::ffi::CStr;
mod event_processor; use std::fmt;
pub mod ffi; use std::marker::PhantomData;
mod ime; use std::mem::MaybeUninit;
mod monitor; use std::ops::Deref;
pub mod util; use std::os::raw::*;
mod window; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
mod xdisplay; use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
mod xsettings; use std::sync::{Arc, Weak};
use std::time::{Duration, Instant};
pub(crate) use self::{ use std::{ptr, slice, str};
monitor::{MonitorHandle, VideoModeHandle},
window::UnownedWindow,
xdisplay::{XConnection, XError, XNotSupported},
};
use calloop::generic::Generic; use calloop::generic::Generic;
use calloop::EventLoop as Loop; use calloop::EventLoop as Loop;
use calloop::{ping::Ping, Readiness}; use calloop::{ping::Ping, Readiness};
use libc::{self, setlocale, LC_CTYPE};
use log::warn; use log::warn;
use std::{ use x11rb::connection::RequestConnection;
cell::{Cell, RefCell}, use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
collections::{HashMap, HashSet}, use x11rb::protocol::xinput::{self, ConnectionExt as _};
ffi::CStr, use x11rb::protocol::xkb;
fmt, use x11rb::protocol::xproto::{self, ConnectionExt as _};
marker::PhantomData,
mem::MaybeUninit,
ops::Deref,
os::{
raw::*,
unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd},
},
ptr,
rc::Rc,
slice, str,
sync::mpsc::{Receiver, Sender, TryRecvError},
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
use libc::{self, setlocale, LC_CTYPE};
use atoms::*;
use x11rb::x11_utils::X11Error as LogicalError; use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::{ use x11rb::xcb_ffi::ReplyOrIdError;
connection::RequestConnection,
protocol::{
xinput::{self, ConnectionExt as _},
xkb,
xproto::{self, ConnectionExt as _},
},
};
use x11rb::{
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
xcb_ffi::ReplyOrIdError,
};
pub(super) use self::util::CustomCursor;
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
};
use super::{common::xkb_state::KbdState, ControlFlow, OsError}; use super::{common::xkb_state::KbdState, ControlFlow, OsError};
use crate::{ use crate::{
error::{EventLoopError, OsError as RootOsError}, error::{EventLoopError, OsError as RootOsError},
@@ -77,6 +38,28 @@ use crate::{
window::WindowAttributes, window::WindowAttributes,
}; };
mod activation;
mod atoms;
mod dnd;
mod event_processor;
pub mod ffi;
mod ime;
mod monitor;
mod util;
mod window;
mod xdisplay;
mod xsettings;
use atoms::*;
use dnd::{Dnd, DndState};
use event_processor::EventProcessor;
use ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender};
pub(crate) use monitor::{MonitorHandle, VideoModeHandle};
use window::UnownedWindow;
pub(crate) use xdisplay::{XConnection, XError, XNotSupported};
pub use util::CustomCursor;
// Xinput constants not defined in x11rb // Xinput constants not defined in x11rb
const ALL_DEVICES: u16 = 0; const ALL_DEVICES: u16 = 0;
const ALL_MASTER_DEVICES: u16 = 1; const ALL_MASTER_DEVICES: u16 = 1;
@@ -150,7 +133,7 @@ pub struct EventLoopWindowTarget {
control_flow: Cell<ControlFlow>, control_flow: Cell<ControlFlow>,
exit: Cell<Option<i32>>, exit: Cell<Option<i32>>,
root: xproto::Window, root: xproto::Window,
ime: RefCell<Ime>, ime: Option<RefCell<Ime>>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>, windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
redraw_sender: WakeSender<WindowId>, redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<ActivationToken>, activation_sender: WakeSender<ActivationToken>,
@@ -166,7 +149,6 @@ pub struct EventLoop<T: 'static> {
user_receiver: PeekableReceiver<T>, user_receiver: PeekableReceiver<T>,
activation_receiver: PeekableReceiver<ActivationToken>, activation_receiver: PeekableReceiver<ActivationToken>,
user_sender: Sender<T>, user_sender: Sender<T>,
target: Rc<RootELW>,
/// The current state of the event loop. /// The current state of the event loop.
state: EventLoopState, state: EventLoopState,
@@ -227,13 +209,15 @@ impl<T: 'static> EventLoop<T> {
setlocale(LC_CTYPE, default_locale); setlocale(LC_CTYPE, default_locale);
} }
} }
let ime = RefCell::new({
let result = Ime::new(Arc::clone(&xconn), ime_event_sender); let ime = Ime::new(Arc::clone(&xconn), ime_event_sender);
if let Err(ImeCreationError::OpenFailure(ref state)) = result { if let Err(ImeCreationError::OpenFailure(state)) = ime.as_ref() {
panic!("Failed to open input method: {state:#?}"); warn!("Failed to open input method: {state:#?}");
} } else if let Err(err) = ime.as_ref() {
result.expect("Failed to set input method destruction callback") warn!("Failed to set input method destruction callback: {err:?}");
}); }
let ime = ime.ok().map(RefCell::new);
let randr_event_offset = xconn let randr_event_offset = xconn
.select_xrandr_input(root) .select_xrandr_input(root)
@@ -324,13 +308,13 @@ impl<T: 'static> EventLoop<T> {
// Set initial device event filter. // Set initial device event filter.
window_target.update_listen_device_events(true); window_target.update_listen_device_events(true);
let target = Rc::new(RootELW { let root_window_target = RootELW {
p: super::EventLoopWindowTarget::X(window_target), p: super::EventLoopWindowTarget::X(window_target),
_marker: PhantomData, _marker: PhantomData,
}); };
let event_processor = EventProcessor { let event_processor = EventProcessor {
target: target.clone(), target: root_window_target,
dnd, dnd,
devices: Default::default(), devices: Default::default(),
randr_event_offset, randr_event_offset,
@@ -349,8 +333,9 @@ impl<T: 'static> EventLoop<T> {
// Register for device hotplug events // Register for device hotplug events
// (The request buffer is flushed during `init_device`) // (The request buffer is flushed during `init_device`)
get_xtarget(&target) let xconn = &EventProcessor::window_target(&event_processor.target).xconn;
.xconn
xconn
.select_xinput_events( .select_xinput_events(
root, root,
ALL_DEVICES, ALL_DEVICES,
@@ -358,8 +343,7 @@ impl<T: 'static> EventLoop<T> {
) )
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events"); .expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
get_xtarget(&target) xconn
.xconn
.select_xkb_events( .select_xkb_events(
0x100, // Use the "core keyboard device" 0x100, // Use the "core keyboard device"
xkb::EventType::NEW_KEYBOARD_NOTIFY xkb::EventType::NEW_KEYBOARD_NOTIFY
@@ -379,7 +363,6 @@ impl<T: 'static> EventLoop<T> {
activation_receiver: PeekableReceiver::from_recv(activation_token_channel), activation_receiver: PeekableReceiver::from_recv(activation_token_channel),
user_receiver: PeekableReceiver::from_recv(user_channel), user_receiver: PeekableReceiver::from_recv(user_channel),
user_sender, user_sender,
target,
state: EventLoopState { state: EventLoopState {
x11_readiness: Readiness::EMPTY, x11_readiness: Readiness::EMPTY,
}, },
@@ -396,7 +379,7 @@ impl<T: 'static> EventLoop<T> {
} }
pub(crate) fn window_target(&self) -> &RootELW { pub(crate) fn window_target(&self) -> &RootELW {
&self.target &self.event_processor.target
} }
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError> pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
@@ -421,7 +404,7 @@ impl<T: 'static> EventLoop<T> {
// `run_on_demand` calls but if they have only just dropped their // `run_on_demand` calls but if they have only just dropped their
// windows we need to make sure those last requests are sent to the // windows we need to make sure those last requests are sent to the
// X Server. // X Server.
let wt = get_xtarget(&self.target); let wt = EventProcessor::window_target(&self.event_processor.target);
wt.x_connection().sync_with_server().map_err(|x_err| { wt.x_connection().sync_with_server().map_err(|x_err| {
EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err))))) EventLoopError::Os(os_error!(OsError::XError(Arc::new(X11Error::Xlib(x_err)))))
})?; })?;
@@ -544,12 +527,12 @@ impl<T: 'static> EventLoop<T> {
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW),
{ {
callback(crate::event::Event::NewEvents(cause), &self.target); callback(Event::NewEvents(cause), &self.event_processor.target);
// NB: For consistency all platforms must emit a 'resumed' event even though X11 // NB: For consistency all platforms must emit a 'resumed' event even though X11
// applications don't themselves have a formal suspend/resume lifecycle. // applications don't themselves have a formal suspend/resume lifecycle.
if cause == StartCause::Init { if cause == StartCause::Init {
callback(crate::event::Event::Resumed, &self.target); callback(Event::Resumed, &self.event_processor.target);
} }
// Process all pending events // Process all pending events
@@ -564,16 +547,16 @@ impl<T: 'static> EventLoop<T> {
}); });
match token { match token {
Some(Ok(token)) => callback( Some(Ok(token)) => {
crate::event::Event::WindowEvent { let event = Event::WindowEvent {
window_id: crate::window::WindowId(window_id), window_id: crate::window::WindowId(window_id),
event: crate::event::WindowEvent::ActivationTokenDone { event: WindowEvent::ActivationTokenDone {
serial, serial,
token: crate::window::ActivationToken::_new(token), token: crate::window::ActivationToken::_new(token),
}, },
}, };
&self.target, callback(event, &self.event_processor.target)
), }
Some(Err(e)) => { Some(Err(e)) => {
log::error!("Failed to get activation token: {}", e); log::error!("Failed to get activation token: {}", e);
} }
@@ -584,7 +567,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer // Empty the user event buffer
{ {
while let Ok(event) = self.user_receiver.try_recv() { while let Ok(event) = self.user_receiver.try_recv() {
callback(crate::event::Event::UserEvent(event), &self.target); callback(Event::UserEvent(event), &self.event_processor.target);
} }
} }
@@ -603,14 +586,14 @@ impl<T: 'static> EventLoop<T> {
window_id, window_id,
event: WindowEvent::RedrawRequested, event: WindowEvent::RedrawRequested,
}, },
&self.target, &self.event_processor.target,
); );
} }
} }
// This is always the last event we dispatch before poll again // This is always the last event we dispatch before poll again
{ {
callback(crate::event::Event::AboutToWait, &self.target); callback(Event::AboutToWait, &self.event_processor.target);
} }
} }
@@ -618,40 +601,44 @@ impl<T: 'static> EventLoop<T> {
where where
F: FnMut(Event<T>, &RootELW), F: FnMut(Event<T>, &RootELW),
{ {
let target = &self.target;
let mut xev = MaybeUninit::uninit(); let mut xev = MaybeUninit::uninit();
let wt = get_xtarget(&self.target);
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } { while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
let mut xev = unsafe { xev.assume_init() }; let mut xev = unsafe { xev.assume_init() };
self.event_processor.process_event(&mut xev, |event| { self.event_processor
if let Event::WindowEvent { .process_event(&mut xev, |window_target, event| {
window_id: crate::window::WindowId(wid), if let Event::WindowEvent {
event: WindowEvent::RedrawRequested, window_id: crate::window::WindowId(wid),
} = event event: WindowEvent::RedrawRequested,
{ } = event
wt.redraw_sender.send(wid).unwrap(); {
} else { let window_target = EventProcessor::window_target(window_target);
callback(event, target); window_target.redraw_sender.send(wid).unwrap();
} } else {
}); callback(event, window_target);
}
});
} }
} }
fn control_flow(&self) -> ControlFlow { fn control_flow(&self) -> ControlFlow {
self.target.p.control_flow() let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.control_flow()
} }
fn exiting(&self) -> bool { fn exiting(&self) -> bool {
self.target.p.exiting() let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.exiting()
} }
fn set_exit_code(&self, code: i32) { fn set_exit_code(&self, code: i32) {
self.target.p.set_exit_code(code) let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.set_exit_code(code);
} }
fn exit_code(&self) -> Option<i32> { fn exit_code(&self) -> Option<i32> {
self.target.p.exit_code() let window_target = EventProcessor::window_target(&self.event_processor.target);
window_target.exit_code()
} }
} }
@@ -667,14 +654,6 @@ impl<T> AsRawFd for EventLoop<T> {
} }
} }
pub(crate) fn get_xtarget(target: &RootELW) -> &EventLoopWindowTarget {
match target.p {
super::EventLoopWindowTarget::X(ref target) => target,
#[cfg(wayland_platform)]
_ => unreachable!(),
}
}
impl EventLoopWindowTarget { impl EventLoopWindowTarget {
/// Returns the `XConnection` of this events loop. /// Returns the `XConnection` of this events loop.
#[inline] #[inline]
@@ -1048,7 +1027,7 @@ fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId {
} }
#[derive(Debug)] #[derive(Debug)]
struct Device { pub struct Device {
_name: String, _name: String,
scroll_axes: Vec<(i32, ScrollAxis)>, scroll_axes: Vec<(i32, ScrollAxis)>,
// For master devices, this is the paired device (pointer <-> keyboard). // For master devices, this is the paired device (pointer <-> keyboard).

View File

@@ -324,7 +324,7 @@ impl XConnection {
} }
} }
pub(crate) struct ScreenResources { pub struct ScreenResources {
/// List of attached modes. /// List of attached modes.
modes: Vec<randr::ModeInfo>, modes: Vec<randr::ModeInfo>,

View File

@@ -39,11 +39,13 @@ impl XConnection {
// Retrieve DPI from Xft.dpi property // Retrieve DPI from Xft.dpi property
pub fn get_xft_dpi(&self) -> Option<f64> { pub fn get_xft_dpi(&self) -> Option<f64> {
// Try to get it from XSETTINGS first. // Try to get it from XSETTINGS first.
match self.xsettings_dpi() { if let Some(xsettings_screen) = self.xsettings_screen() {
Ok(Some(dpi)) => return Some(dpi), match self.xsettings_dpi(xsettings_screen) {
Ok(None) => {} Ok(Some(dpi)) => return Some(dpi),
Err(err) => { Ok(None) => {}
log::warn!("failed to fetch XSettings: {err}"); Err(err) => {
log::warn!("failed to fetch XSettings: {err}");
}
} }
} }

View File

@@ -123,7 +123,7 @@ impl SharedState {
unsafe impl Send for UnownedWindow {} unsafe impl Send for UnownedWindow {}
unsafe impl Sync for UnownedWindow {} unsafe impl Sync for UnownedWindow {}
pub(crate) struct UnownedWindow { pub struct UnownedWindow {
pub(crate) xconn: Arc<XConnection>, // never changes pub(crate) xconn: Arc<XConnection>, // never changes
xwindow: xproto::Window, // never changes xwindow: xproto::Window, // never changes
#[allow(dead_code)] #[allow(dead_code)]
@@ -555,9 +555,9 @@ impl UnownedWindow {
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask)) leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
.ignore_error(); .ignore_error();
{ // Try to create input context for the window.
let result = event_loop if let Some(ime) = event_loop.ime.as_ref() {
.ime let result = ime
.borrow_mut() .borrow_mut()
.create_context(window.xwindow as ffi::Window, false); .create_context(window.xwindow as ffi::Window, false);
leap!(result); leap!(result);

View File

@@ -22,7 +22,7 @@ use x11rb::{
}; };
/// A connection to an X server. /// A connection to an X server.
pub(crate) struct XConnection { pub struct XConnection {
pub xlib: ffi::Xlib, pub xlib: ffi::Xlib,
pub xcursor: ffi::Xcursor, pub xcursor: ffi::Xcursor,
@@ -59,7 +59,7 @@ pub(crate) struct XConnection {
randr_version: (u32, u32), randr_version: (u32, u32),
/// Atom for the XSettings screen. /// Atom for the XSettings screen.
xsettings_screen: xproto::Atom, xsettings_screen: Option<xproto::Atom>,
pub latest_error: Mutex<Option<XError>>, pub latest_error: Mutex<Option<XError>>,
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>, pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
@@ -108,21 +108,6 @@ impl XConnection {
// Get the default screen. // Get the default screen.
let default_screen = unsafe { (xlib.XDefaultScreen)(display) } as usize; let default_screen = unsafe { (xlib.XDefaultScreen)(display) } as usize;
// Fetch the _XSETTINGS_S[screen number] atom.
let xsettings_screen = xcb
.intern_atom(false, format!("_XSETTINGS_S{}", default_screen).as_bytes())
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
// Fetch the other atoms.
let atoms = Atoms::new(&xcb)
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
.reply()
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
let xsettings_screen = xsettings_screen
.reply()
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
.atom;
// Load the database. // Load the database.
let database = resource_manager::new_from_default(&xcb) let database = resource_manager::new_from_default(&xcb)
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?; .map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
@@ -134,23 +119,16 @@ impl XConnection {
.reply() .reply()
.expect("failed to query XRandR version"); .expect("failed to query XRandR version");
// Get PropertyNotify events from the XSETTINGS window. let xsettings_screen = Self::new_xsettings_screen(&xcb, default_screen);
// TODO: The XSETTINGS window here can change. In the future, listen for DestroyNotify on this window if xsettings_screen.is_none() {
// in order to accomodate for a changed window here. log::warn!("error setting XSETTINGS; Xft options won't reload automatically")
let selector_window = xcb }
.get_selection_owner(xsettings_screen)
// Fetch atoms.
let atoms = Atoms::new(&xcb)
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))? .map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
.reply() .reply()
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))? .map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
.owner;
xcb.change_window_attributes(
selector_window,
&xproto::ChangeWindowAttributesAux::new()
.event_mask(xproto::EventMask::PROPERTY_CHANGE),
)
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
.check()
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
Ok(XConnection { Ok(XConnection {
xlib, xlib,
@@ -170,6 +148,37 @@ impl XConnection {
}) })
} }
fn new_xsettings_screen(xcb: &XCBConnection, default_screen: usize) -> Option<xproto::Atom> {
// Fetch the _XSETTINGS_S[screen number] atom.
let xsettings_screen = xcb
.intern_atom(false, format!("_XSETTINGS_S{}", default_screen).as_bytes())
.ok()?
.reply()
.ok()?
.atom;
// Get PropertyNotify events from the XSETTINGS window.
// TODO: The XSETTINGS window here can change. In the future, listen for DestroyNotify on this window
// in order to accomodate for a changed window here.
let selector_window = xcb
.get_selection_owner(xsettings_screen)
.ok()?
.reply()
.ok()?
.owner;
xcb.change_window_attributes(
selector_window,
&xproto::ChangeWindowAttributesAux::new()
.event_mask(xproto::EventMask::PROPERTY_CHANGE),
)
.ok()?
.check()
.ok()?;
Some(xsettings_screen)
}
/// Checks whether an error has been triggered by the previous function calls. /// Checks whether an error has been triggered by the previous function calls.
#[inline] #[inline]
pub fn check_errors(&self) -> Result<(), XError> { pub fn check_errors(&self) -> Result<(), XError> {
@@ -258,7 +267,7 @@ impl XConnection {
/// Get the atom for Xsettings. /// Get the atom for Xsettings.
#[inline] #[inline]
pub fn xsettings_screen(&self) -> u32 { pub fn xsettings_screen(&self) -> Option<xproto::Atom> {
self.xsettings_screen self.xsettings_screen
} }
} }
@@ -300,7 +309,7 @@ impl fmt::Display for XError {
/// Error returned if this system doesn't have XLib or can't create an X connection. /// Error returned if this system doesn't have XLib or can't create an X connection.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) enum XNotSupported { pub enum XNotSupported {
/// Failed to load one or several shared libraries. /// Failed to load one or several shared libraries.
LibraryOpenError(ffi::OpenError), LibraryOpenError(ffi::OpenError),

View File

@@ -4,13 +4,13 @@
//! //!
//! [here]: https://github.com/derat/xsettingsd //! [here]: https://github.com/derat/xsettingsd
use super::{atoms::*, XConnection};
use x11rb::protocol::xproto::ConnectionExt;
use std::iter; use std::iter;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
use x11rb::protocol::xproto::{self, ConnectionExt};
use super::{atoms::*, XConnection};
type Result<T> = core::result::Result<T, ParserError>; type Result<T> = core::result::Result<T, ParserError>;
const DPI_NAME: &[u8] = b"Xft/DPI"; const DPI_NAME: &[u8] = b"Xft/DPI";
@@ -20,13 +20,16 @@ const BIG_ENDIAN: u8 = b'B';
impl XConnection { impl XConnection {
/// Get the DPI from XSettings. /// Get the DPI from XSettings.
pub(crate) fn xsettings_dpi(&self) -> core::result::Result<Option<f64>, super::X11Error> { pub(crate) fn xsettings_dpi(
&self,
xsettings_screen: xproto::Atom,
) -> core::result::Result<Option<f64>, super::X11Error> {
let atoms = self.atoms(); let atoms = self.atoms();
// Get the current owner of the screen's settings. // Get the current owner of the screen's settings.
let owner = self let owner = self
.xcb_connection() .xcb_connection()
.get_selection_owner(self.xsettings_screen())? .get_selection_owner(xsettings_screen)?
.reply()?; .reply()?;
// Read the _XSETTINGS_SETTINGS property. // Read the _XSETTINGS_SETTINGS property.

View File

@@ -9,8 +9,8 @@ use std::{
use bitflags::bitflags; use bitflags::bitflags;
use orbclient::{ use orbclient::{
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MoveEvent, QuitEvent, ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
ResizeEvent, ScrollEvent, TextInputEvent, MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
}; };
use smol_str::SmolStr; use smol_str::SmolStr;
@@ -457,6 +457,14 @@ impl<T: 'static> EventLoop<T> {
}, },
}); });
} }
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
event_handler(event::Event::DeviceEvent {
device_id: event::DeviceId(DeviceId),
event: event::DeviceEvent::MouseMotion {
delta: (dx as f64, dy as f64),
},
});
}
EventOption::Button(ButtonEvent { EventOption::Button(ButtonEvent {
left, left,
middle, middle,
@@ -510,7 +518,7 @@ impl<T: 'static> EventLoop<T> {
// Acknowledge resize after event loop. // Acknowledge resize after event loop.
event_state.resize_opt = Some((width, height)); event_state.resize_opt = Some((width, height));
} }
//TODO: Clipboard //TODO: Screen, Clipboard, Drop
EventOption::Hover(HoverEvent { entered }) => { EventOption::Hover(HoverEvent { entered }) => {
if entered { if entered {
event_handler(event::Event::WindowEvent { event_handler(event::Event::WindowEvent {

View File

@@ -13,7 +13,8 @@ use crate::{
}; };
use super::{ use super::{
EventLoopWindowTarget, MonitorHandle, RedoxSocket, TimeSocket, WindowId, WindowProperties, EventLoopWindowTarget, MonitorHandle, OsError, RedoxSocket, TimeSocket, WindowId,
WindowProperties,
}; };
// These values match the values uses in the `window_new` function in orbital: // These values match the values uses in the `window_new` function in orbital:
@@ -21,7 +22,9 @@ use super::{
const ORBITAL_FLAG_ASYNC: char = 'a'; const ORBITAL_FLAG_ASYNC: char = 'a';
const ORBITAL_FLAG_BACK: char = 'b'; const ORBITAL_FLAG_BACK: char = 'b';
const ORBITAL_FLAG_FRONT: char = 'f'; const ORBITAL_FLAG_FRONT: char = 'f';
const ORBITAL_FLAG_HIDDEN: char = 'h';
const ORBITAL_FLAG_BORDERLESS: char = 'l'; const ORBITAL_FLAG_BORDERLESS: char = 'l';
const ORBITAL_FLAG_MAXIMIZED: char = 'm';
const ORBITAL_FLAG_RESIZABLE: char = 'r'; const ORBITAL_FLAG_RESIZABLE: char = 'r';
const ORBITAL_FLAG_TRANSPARENT: char = 't'; const ORBITAL_FLAG_TRANSPARENT: char = 't';
@@ -57,11 +60,15 @@ impl Window {
// Async by default. // Async by default.
let mut flag_str = ORBITAL_FLAG_ASYNC.to_string(); let mut flag_str = ORBITAL_FLAG_ASYNC.to_string();
if attrs.maximized {
flag_str.push(ORBITAL_FLAG_MAXIMIZED);
}
if attrs.resizable { if attrs.resizable {
flag_str.push(ORBITAL_FLAG_RESIZABLE); flag_str.push(ORBITAL_FLAG_RESIZABLE);
} }
//TODO: maximized, fullscreen, visible //TODO: fullscreen
if attrs.transparent { if attrs.transparent {
flag_str.push(ORBITAL_FLAG_TRANSPARENT); flag_str.push(ORBITAL_FLAG_TRANSPARENT);
@@ -71,6 +78,10 @@ impl Window {
flag_str.push(ORBITAL_FLAG_BORDERLESS); flag_str.push(ORBITAL_FLAG_BORDERLESS);
} }
if !attrs.visible {
flag_str.push(ORBITAL_FLAG_HIDDEN);
}
match attrs.window_level { match attrs.window_level {
window::WindowLevel::AlwaysOnBottom => { window::WindowLevel::AlwaysOnBottom => {
flag_str.push(ORBITAL_FLAG_BACK); flag_str.push(ORBITAL_FLAG_BACK);
@@ -129,6 +140,23 @@ impl Window {
f(self) f(self)
} }
fn get_flag(&self, flag: char) -> Result<bool, error::ExternalError> {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
let properties = WindowProperties::new(path);
Ok(properties.flags.contains(flag))
}
fn set_flag(&self, flag: char, value: bool) -> Result<(), error::ExternalError> {
self.window_socket
.write(format!("F,{flag},{}", if value { 1 } else { 0 }).as_bytes())
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
Ok(())
}
#[inline] #[inline]
pub fn id(&self) -> WindowId { pub fn id(&self) -> WindowId {
WindowId { WindowId {
@@ -254,17 +282,21 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_transparent(&self, _transparent: bool) {} pub fn set_transparent(&self, transparent: bool) {
let _ = self.set_flag(ORBITAL_FLAG_TRANSPARENT, transparent);
}
#[inline] #[inline]
pub fn set_blur(&self, _blur: bool) {} pub fn set_blur(&self, _blur: bool) {}
#[inline] #[inline]
pub fn set_visible(&self, _visibility: bool) {} pub fn set_visible(&self, visible: bool) {
let _ = self.set_flag(ORBITAL_FLAG_HIDDEN, !visible);
}
#[inline] #[inline]
pub fn is_visible(&self) -> Option<bool> { pub fn is_visible(&self) -> Option<bool> {
None Some(!self.get_flag(ORBITAL_FLAG_HIDDEN).unwrap_or(false))
} }
#[inline] #[inline]
@@ -276,17 +308,13 @@ impl Window {
pub fn set_resize_increments(&self, _increments: Option<Size>) {} pub fn set_resize_increments(&self, _increments: Option<Size>) {}
#[inline] #[inline]
pub fn set_resizable(&self, _resizeable: bool) {} pub fn set_resizable(&self, resizeable: bool) {
let _ = self.set_flag(ORBITAL_FLAG_RESIZABLE, resizeable);
}
#[inline] #[inline]
pub fn is_resizable(&self) -> bool { pub fn is_resizable(&self) -> bool {
let mut buf: [u8; 4096] = [0; 4096]; self.get_flag(ORBITAL_FLAG_RESIZABLE).unwrap_or(false)
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
properties.flags.contains(ORBITAL_FLAG_RESIZABLE)
} }
#[inline] #[inline]
@@ -298,11 +326,13 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_maximized(&self, _maximized: bool) {} pub fn set_maximized(&self, maximized: bool) {
let _ = self.set_flag(ORBITAL_FLAG_MAXIMIZED, maximized);
}
#[inline] #[inline]
pub fn is_maximized(&self) -> bool { pub fn is_maximized(&self) -> bool {
false self.get_flag(ORBITAL_FLAG_MAXIMIZED).unwrap_or(false)
} }
#[inline] #[inline]
@@ -314,21 +344,30 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_decorations(&self, _decorations: bool) {} pub fn set_decorations(&self, decorations: bool) {
let _ = self.set_flag(ORBITAL_FLAG_BORDERLESS, !decorations);
#[inline]
pub fn is_decorated(&self) -> bool {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
!properties.flags.contains(ORBITAL_FLAG_BORDERLESS)
} }
#[inline] #[inline]
pub fn set_window_level(&self, _level: window::WindowLevel) {} pub fn is_decorated(&self) -> bool {
!self.get_flag(ORBITAL_FLAG_BORDERLESS).unwrap_or(false)
}
#[inline]
pub fn set_window_level(&self, level: window::WindowLevel) {
match level {
window::WindowLevel::AlwaysOnBottom => {
let _ = self.set_flag(ORBITAL_FLAG_BACK, true);
}
window::WindowLevel::Normal => {
let _ = self.set_flag(ORBITAL_FLAG_BACK, false);
let _ = self.set_flag(ORBITAL_FLAG_FRONT, false);
}
window::WindowLevel::AlwaysOnTop => {
let _ = self.set_flag(ORBITAL_FLAG_FRONT, true);
}
}
}
#[inline] #[inline]
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {} pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
@@ -359,30 +398,58 @@ impl Window {
} }
#[inline] #[inline]
pub fn set_cursor_grab(&self, _: window::CursorGrabMode) -> Result<(), error::ExternalError> { pub fn set_cursor_grab(
Err(error::ExternalError::NotSupported( &self,
error::NotSupportedError::new(), mode: window::CursorGrabMode,
)) ) -> Result<(), error::ExternalError> {
let (grab, relative) = match mode {
window::CursorGrabMode::None => (false, false),
window::CursorGrabMode::Confined => (true, false),
window::CursorGrabMode::Locked => (true, true),
};
self.window_socket
.write(format!("M,G,{}", if grab { 1 } else { 0 }).as_bytes())
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
self.window_socket
.write(format!("M,R,{}", if relative { 1 } else { 0 }).as_bytes())
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
Ok(())
} }
#[inline] #[inline]
pub fn set_cursor_visible(&self, _: bool) {} pub fn set_cursor_visible(&self, visible: bool) {
let _ = self
.window_socket
.write(format!("M,C,{}", if visible { 1 } else { 0 }).as_bytes());
}
#[inline] #[inline]
pub fn drag_window(&self) -> Result<(), error::ExternalError> { pub fn drag_window(&self) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported( self.window_socket
error::NotSupportedError::new(), .write(b"D")
)) .map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
Ok(())
} }
#[inline] #[inline]
pub fn drag_resize_window( pub fn drag_resize_window(
&self, &self,
_direction: window::ResizeDirection, direction: window::ResizeDirection,
) -> Result<(), error::ExternalError> { ) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported( let arg = match direction {
error::NotSupportedError::new(), window::ResizeDirection::East => "R",
)) window::ResizeDirection::North => "T",
window::ResizeDirection::NorthEast => "T,R",
window::ResizeDirection::NorthWest => "T,L",
window::ResizeDirection::South => "B",
window::ResizeDirection::SouthEast => "B,R",
window::ResizeDirection::SouthWest => "B,L",
window::ResizeDirection::West => "L",
};
self.window_socket
.write(format!("D,{}", arg).as_bytes())
.map_err(|err| error::ExternalError::Os(os_error!(OsError::new(err))))?;
Ok(())
} }
#[inline] #[inline]

View File

@@ -57,8 +57,11 @@ use serde::{Deserialize, Serialize};
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// **Web:** The [`Window`], which is represented by a `HTMLElementCanvas`, can /// - **Web:** The window is represented by a `HTMLElementCanvas`, and cannot
/// not be closed by dropping the [`Window`]. /// currently be closed by dropping the [`Window`].
/// - **Android:** Each window is spawned inside its own process, and as such
/// Winit does not support multiple windows on Android. Create a new
/// activity instead.
pub struct Window { pub struct Window {
pub(crate) window: platform_impl::Window, pub(crate) window: platform_impl::Window,
} }
@@ -946,7 +949,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Web / iOS / Android / Orbital:** Unsupported. /// - **Web / iOS / Android:** Unsupported.
/// - **X11:** Can only be set while building the window, with [`WindowBuilder::with_transparent`]. /// - **X11:** Can only be set while building the window, with [`WindowBuilder::with_transparent`].
#[inline] #[inline]
pub fn set_transparent(&self, transparent: bool) { pub fn set_transparent(&self, transparent: bool) {
@@ -1079,7 +1082,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web:** Unsupported.
#[inline] #[inline]
pub fn set_maximized(&self, maximized: bool) { pub fn set_maximized(&self, maximized: bool) {
self.window self.window
@@ -1090,7 +1093,7 @@ impl Window {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web:** Unsupported.
#[inline] #[inline]
pub fn is_maximized(&self) -> bool { pub fn is_maximized(&self) -> bool {
self.window.maybe_wait_on_main(|w| w.is_maximized()) self.window.maybe_wait_on_main(|w| w.is_maximized())
@@ -1450,7 +1453,7 @@ impl Window {
/// - **Wayland:** The cursor is only hidden within the confines of the window. /// - **Wayland:** The cursor is only hidden within the confines of the window.
/// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is /// - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
/// outside of the window. /// outside of the window.
/// - **iOS / Android / Orbital:** Unsupported. /// - **iOS / Android:** Unsupported.
#[inline] #[inline]
pub fn set_cursor_visible(&self, visible: bool) { pub fn set_cursor_visible(&self, visible: bool) {
self.window self.window
@@ -1467,7 +1470,7 @@ impl Window {
/// - **X11:** Un-grabs the cursor. /// - **X11:** Un-grabs the cursor.
/// - **Wayland:** Requires the cursor to be inside the window to be dragged. /// - **Wayland:** Requires the cursor to be inside the window to be dragged.
/// - **macOS:** May prevent the button release event to be triggered. /// - **macOS:** May prevent the button release event to be triggered.
/// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> { pub fn drag_window(&self) -> Result<(), ExternalError> {
self.window.maybe_wait_on_main(|w| w.drag_window()) self.window.maybe_wait_on_main(|w| w.drag_window())
@@ -1481,7 +1484,7 @@ impl Window {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS:** Always returns an [`ExternalError::NotSupported`] /// - **macOS:** Always returns an [`ExternalError::NotSupported`]
/// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> { pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.window self.window
@@ -1642,7 +1645,7 @@ pub enum CursorGrabMode {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. /// - **macOS:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
/// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
Confined, Confined,
/// The cursor is locked inside the window area to the certain position. /// The cursor is locked inside the window area to the certain position.
@@ -1653,7 +1656,7 @@ pub enum CursorGrabMode {
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now. /// - **X11 / Windows:** Not implemented. Always returns [`ExternalError::NotSupported`] for now.
/// - **iOS / Android / Orbital:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android:** Always returns an [`ExternalError::NotSupported`].
Locked, Locked,
} }