mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
Add dragging window with cursor feature (#1840)
* X11 implementation. * Introduce example. * Wayland implementation. * Windows implementation. * Improve Wayland seat passing. * MacOS implementation. * Correct windows implementation per specification. * Update dependency smithay-client-toolkit from branch to master. * Fixed blocking thread in windows implementation. * Add multi-window example. * Move Wayland to a different PR. * Fix CHANGELOG. * Improve example. Co-authored-by: Markus Røyset <maroider@protonmail.com> * Rename `set_drag_window` to `begin_drag`. * Improve example. * Fix CHANGELOG. * Fix CHANGELOG. Co-authored-by: Markus Røyset <maroider@protonmail.com> * Rename to `drag_window`. * Fix typo. * Re-introduce Wayland implementation. * Fixing Wayland build. * Fixing Wayland build. * Move SCTK to 0.12.3. Co-authored-by: Markus Røyset <maroider@protonmail.com>
This commit is contained in:
parent
4192d04a53
commit
98470393d1
|
@ -13,6 +13,7 @@
|
|||
- On Android, bump `ndk` and `ndk-glue` to 0.3: use predefined constants for event `ident`.
|
||||
- On Windows, fixed `WindowEvent::ThemeChanged` not properly firing and fixed `Window::theme` returning the wrong theme.
|
||||
- On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements.
|
||||
- Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland.
|
||||
|
||||
# 0.24.0 (2020-12-09)
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ features = [
|
|||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.28", features = [ "dlopen"] , optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.12", optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.12.3", optional = true }
|
||||
mio = { version = "0.6", optional = true }
|
||||
mio-extras = { version = "2.0", optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
|
|
|
@ -209,6 +209,7 @@ Legend:
|
|||
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |
|
||||
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |
|
||||
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |
|
||||
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|
||||
|
||||
### Pending API Reworks
|
||||
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|
||||
|
|
73
examples/drag_window.rs
Normal file
73
examples/drag_window.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{
|
||||
ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent,
|
||||
},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
let mut switched = false;
|
||||
let mut entered_id = window_2.id();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
eprintln!("Switch which window is to be dragged by pressing \"x\".")
|
||||
}
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
let window = if (window_id == window_1.id() && switched)
|
||||
|| (window_id == window_2.id() && !switched)
|
||||
{
|
||||
&window_2
|
||||
} else {
|
||||
&window_1
|
||||
};
|
||||
|
||||
window.drag_window().unwrap()
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
entered_id = window_id;
|
||||
name_windows(entered_id, switched, &window_1, &window_2)
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(VirtualKeyCode::X),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
switched = !switched;
|
||||
name_windows(entered_id, switched, &window_1, &window_2);
|
||||
println!("Switched!")
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
|
||||
let (drag_target, other) =
|
||||
if (window_id == window_1.id() && switched) || (window_id == window_2.id() && !switched) {
|
||||
(&window_2, &window_1)
|
||||
} else {
|
||||
(&window_1, &window_2)
|
||||
};
|
||||
drag_target.set_title("drag target");
|
||||
other.set_title("winit window");
|
||||
}
|
|
@ -556,6 +556,12 @@ impl Window {
|
|||
|
||||
pub fn set_cursor_visible(&self, _: bool) {}
|
||||
|
||||
pub fn drag_window(&self) -> Result<(), error::ExternalError> {
|
||||
Err(error::ExternalError::NotSupported(
|
||||
error::NotSupportedError::new(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
||||
let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() {
|
||||
unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }
|
||||
|
|
|
@ -182,6 +182,10 @@ impl Inner {
|
|||
debug!("`Window::set_cursor_visible` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
warn!("`Window::set_minimized` is ignored on iOS")
|
||||
}
|
||||
|
|
|
@ -358,6 +358,11 @@ impl Window {
|
|||
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)
|
||||
|
|
|
@ -4,6 +4,7 @@ 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;
|
||||
|
@ -28,6 +29,7 @@ pub(super) fn handle_pointer(
|
|||
event: PointerEvent,
|
||||
pointer_data: &Rc<RefCell<PointerData>>,
|
||||
winit_state: &mut WinitState,
|
||||
seat: WlSeat,
|
||||
) {
|
||||
let event_sink = &mut winit_state.event_sink;
|
||||
let mut pointer_data = pointer_data.borrow_mut();
|
||||
|
@ -59,6 +61,7 @@ pub(super) fn handle_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);
|
||||
|
||||
|
@ -101,6 +104,7 @@ pub(super) fn handle_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);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use sctk::reexports::protocols::unstable::pointer_constraints::v1::client::zwp_p
|
|||
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;
|
||||
|
@ -35,6 +36,9 @@ pub struct WinitPointer {
|
|||
|
||||
/// Latest observed serial in pointer events.
|
||||
latest_serial: Rc<Cell<u32>>,
|
||||
|
||||
/// Seat.
|
||||
seat: WlSeat,
|
||||
}
|
||||
|
||||
impl PartialEq for WinitPointer {
|
||||
|
@ -144,6 +148,10 @@ impl WinitPointer {
|
|||
confined_pointer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drag_window(&self, window: &Window<ConceptFrame>) {
|
||||
window.start_interactive_move(&self.seat, self.latest_serial.get());
|
||||
}
|
||||
}
|
||||
|
||||
/// A pointer wrapper for easy releasing and managing pointers.
|
||||
|
@ -172,11 +180,18 @@ impl Pointers {
|
|||
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::<WinitState>().unwrap();
|
||||
handlers::handle_pointer(pointer, event, &pointer_data, winit_state);
|
||||
handlers::handle_pointer(
|
||||
pointer,
|
||||
event,
|
||||
&pointer_data,
|
||||
winit_state,
|
||||
pointer_seat.clone(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -586,6 +586,18 @@ impl Window {
|
|||
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;
|
||||
|
|
|
@ -34,6 +34,9 @@ pub enum WindowRequest {
|
|||
/// Grab cursor.
|
||||
GrabCursor(bool),
|
||||
|
||||
/// Drag window.
|
||||
DragWindow,
|
||||
|
||||
/// Maximize the window.
|
||||
Maximize(bool),
|
||||
|
||||
|
@ -268,6 +271,12 @@ impl WindowHandle {
|
|||
pointer.set_cursor(Some(cursor_icon));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drag_window(&self) {
|
||||
for pointer in self.pointers.iter() {
|
||||
pointer.drag_window(&self.window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -299,6 +308,9 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
|
|||
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();
|
||||
|
|
|
@ -1276,6 +1276,46 @@ impl UnownedWindow {
|
|||
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
|
||||
|
|
|
@ -636,6 +636,16 @@ impl UnownedWindow {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
unsafe {
|
||||
let event: id = msg_send![NSApp(), currentEvent];
|
||||
let _: () = msg_send![*self.ns_window, performWindowDragWithEvent: event];
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn is_zoomed(&self) -> bool {
|
||||
// because `isZoomed` doesn't work if the window's borderless,
|
||||
// we make it resizable temporalily.
|
||||
|
|
|
@ -222,6 +222,11 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
Err(ExternalError::NotSupported(NotSupportedError::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_minimized(&self, _minimized: bool) {
|
||||
// Intentionally a no-op, as canvases cannot be 'minimized'
|
||||
|
|
|
@ -14,8 +14,8 @@ use std::{
|
|||
use winapi::{
|
||||
ctypes::c_int,
|
||||
shared::{
|
||||
minwindef::{HINSTANCE, UINT},
|
||||
windef::{HWND, POINT, RECT},
|
||||
minwindef::{HINSTANCE, LPARAM, UINT, WPARAM},
|
||||
windef::{HWND, POINT, POINTS, RECT},
|
||||
},
|
||||
um::{
|
||||
combaseapi, dwmapi,
|
||||
|
@ -26,7 +26,7 @@ use winapi::{
|
|||
oleidl::LPDROPTARGET,
|
||||
shobjidl_core::{CLSID_TaskbarList, ITaskbarList2},
|
||||
wingdi::{CreateRectRgn, DeleteObject},
|
||||
winnt::LPCWSTR,
|
||||
winnt::{LPCWSTR, SHORT},
|
||||
winuser,
|
||||
},
|
||||
};
|
||||
|
@ -357,6 +357,30 @@ impl Window {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
unsafe {
|
||||
let points = {
|
||||
let mut pos = mem::zeroed();
|
||||
winuser::GetCursorPos(&mut pos);
|
||||
pos
|
||||
};
|
||||
let points = POINTS {
|
||||
x: points.x as SHORT,
|
||||
y: points.y as SHORT,
|
||||
};
|
||||
winuser::ReleaseCapture();
|
||||
winuser::PostMessageW(
|
||||
self.window.0,
|
||||
winuser::WM_NCLBUTTONDOWN,
|
||||
winuser::HTCAPTION as WPARAM,
|
||||
&points as *const _ as LPARAM,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.window.0)
|
||||
|
|
|
@ -764,6 +764,22 @@ impl Window {
|
|||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
self.window.set_cursor_visible(visible)
|
||||
}
|
||||
|
||||
/// Moves the window with the left mouse button until the button is released.
|
||||
///
|
||||
/// There's no guarantee that this will work unless the left mouse button was pressed
|
||||
/// immediately before this function is called.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** Un-grabs the cursor.
|
||||
/// - **Wayland:** Requires the cursor to be inside the window to be dragged.
|
||||
/// - **macOS:** May prevent the button release event to be triggered.
|
||||
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
|
||||
#[inline]
|
||||
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
||||
self.window.drag_window()
|
||||
}
|
||||
}
|
||||
|
||||
/// Monitor info functions.
|
||||
|
|
Loading…
Reference in a new issue