Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with an improved event design #1374

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1d9c2a4
Draft improved event API
Osspial Jan 6, 2020
46176f3
Modify press flags
Osspial Jan 7, 2020
896f042
Unify KeyPress and PointerPress flags
Osspial Jan 7, 2020
761e456
Add AppEvent and ScrollStarted/Ended events
Osspial Jan 8, 2020
986be4e
Rename Key to LogicalKey
Osspial Jan 8, 2020
0afc10c
Implement opaque press events
Osspial May 15, 2020
68761e6
Address @Ralith's feedback
Osspial May 16, 2020
6024560
Redefine PointerButton
Osspial May 16, 2020
63dae5d
Make this compile on Windows
Osspial May 16, 2020
5f7cf2a
Reorder events.rs
Osspial May 16, 2020
6adde34
Make examples, docs, and tests work
Osspial May 16, 2020
0c3bc38
format all the things
Osspial May 16, 2020
d06f58f
Make serde work
Osspial May 16, 2020
510f005
Make serde tests work
Osspial May 17, 2020
bb5dd6b
Rename KeyPress to Key
Osspial May 20, 2020
852b292
Rename PointerPress to PointerButton
Osspial May 20, 2020
9f3fc7b
Format
Osspial May 20, 2020
f031607
Add constructors and setters to event structs
Osspial May 20, 2020
304cbf3
Reorder some things
Osspial May 20, 2020
8ef7b1e
Add PointerButton serialization styles support
Osspial May 21, 2020
b9e152a
Add pen events to the public API
Osspial May 22, 2020
b5bf39a
Fully implement new pointer API on Windows
Osspial May 22, 2020
851a7ea
Distinguish between touch and pen IDs
Osspial May 22, 2020
61f2934
Implement new pen API on Windows
Osspial May 25, 2020
eea84e3
Remove redundant pointermoved from wm_touch handler
Osspial May 25, 2020
4db1d6e
Fix Pointer(Created|Entered) events being sent after PointerDestroyed
Osspial May 25, 2020
201de6c
Include scroll events in example
Osspial May 25, 2020
253df57
Remove Pointer from Scroll events
Osspial May 25, 2020
23ce444
Use WM_POINTER for mouse events
Osspial May 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ fn main() {
WindowEvent::PointerButton(..) |
WindowEvent::PointerEntered(..) |
WindowEvent::PointerLeft(..) |
WindowEvent::ScrollStarted |
WindowEvent::ScrollLines(..) |
WindowEvent::ScrollPixels(..) |
WindowEvent::ScrollEnded |
WindowEvent::PointerDestroyed(..) => println!("{:?}", e),
_ => ()
},
Event::MainEventsCleared => window.request_redraw(),
Event::MainEventsCleared => {
window.request_redraw()
},
_ => (),
}
});
Expand Down
27 changes: 14 additions & 13 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,13 @@ pub enum WindowEvent<'a> {
// TODO: IMPLEMENT. IS THIS REASONABLE TO IMPLEMENT ON NON-WINDOWS PLATFORMS?
// PointerHovered(PointerId),
PointerButton(PointerId, PointerButtonEvent),
PointerScrollStarted(PointerId),
PointerScrollLines(PointerId, UnitlessDelta<f64>),
PointerScrollPixels(PointerId, PhysicalDelta<f64>),
PointerScrollEnded(PointerId),
PointerLeft(PointerId),
PointerDestroyed(PointerId),
PointerLeft(PointerId),

ScrollStarted,
ScrollLines(UnitlessDelta<f64>),
ScrollPixels(PhysicalDelta<f64>),
ScrollEnded,
Comment on lines +187 to +190
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wonder the idea behind event for start/stop scrolling, seems like events for ScrooLine and ScrollPixels are already enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We exposed a TouchPhase field on the old MouseWheel event and ScrollStarted/ScrollEnded allow us to expose that functionality in the new API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the naming/docs are pretty confusing about those. The docs are saying that they are about touchscreen, in reality they are used for some mouse/touchpad scroll as well. Also old API is using TouchPhase::Ended, when you've removed all your fingers from the touch surface, so using scroll here is just confusing. If we take into account the other Pointer events handling of touch is just more confusing at this point, unless you want to force users to track touch phase themself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that implying that the scroll phase and the touchscreen touch phase are related is confusing, why is why this redesign doesn't do that. TouchPhase in MouseWheel was originally exposed in order to implement overscrolling on macOS, and this design should let us continue to do that. The exact API guarantees around when ScrollStarted and ScrollEnded get emitted should be specified in the docs, though.


/// The system window theme has changed.
///
Expand Down Expand Up @@ -309,10 +310,10 @@ impl Clone for WindowEvent<'static> {
PointerEntered(id) => PointerEntered(id),
PointerLeft(id) => PointerLeft(id),
PointerDestroyed(id) => PointerDestroyed(id),
PointerScrollStarted(id) => PointerScrollStarted(id),
PointerScrollLines(id, delta) => PointerScrollLines(id, delta),
PointerScrollPixels(id, delta) => PointerScrollPixels(id, delta),
PointerScrollEnded(id) => PointerScrollEnded(id),
ScrollStarted => ScrollStarted,
ScrollLines(delta) => ScrollLines(delta),
ScrollPixels(delta) => ScrollPixels(delta),
ScrollEnded => ScrollEnded,
ThemeChanged(theme) => ThemeChanged(theme),
ScaleFactorChanged(..) => {
unreachable!("Static event can't be about scale factor changing")
Expand Down Expand Up @@ -347,10 +348,10 @@ impl<'a> WindowEvent<'a> {
PointerEntered(id) => Ok(PointerEntered(id)),
PointerLeft(id) => Ok(PointerLeft(id)),
PointerDestroyed(id) => Ok(PointerDestroyed(id)),
PointerScrollStarted(id) => Ok(PointerScrollStarted(id)),
PointerScrollLines(id, delta) => Ok(PointerScrollLines(id, delta)),
PointerScrollPixels(id, delta) => Ok(PointerScrollPixels(id, delta)),
PointerScrollEnded(id) => Ok(PointerScrollEnded(id)),
ScrollStarted => Ok(ScrollStarted),
ScrollLines(delta) => Ok(ScrollLines(delta)),
ScrollPixels(delta) => Ok(ScrollPixels(delta)),
ScrollEnded => Ok(ScrollEnded),
ThemeChanged(theme) => Ok(ThemeChanged(theme)),
ScaleFactorChanged(..) => Err(self),
}
Expand Down
17 changes: 15 additions & 2 deletions src/platform_impl/windows/event.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(non_snake_case)]

use std::{
char,
os::raw::c_int,
Expand All @@ -14,7 +16,7 @@ use std::collections::hash_map::{HashMap, Entry};
use winapi::{
shared::{
windef::HWND,
minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM, DWORD},
minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM, DWORD, BOOL},
},
um::{winuser, winreg},
};
Expand Down Expand Up @@ -484,6 +486,14 @@ impl Default for PointerState {
}
}

type EnableMouseInPointer =
unsafe extern "system" fn(fEnable: BOOL) -> BOOL;

lazy_static! {
static ref ENABLE_MOUSE_IN_POINTER: Option<EnableMouseInPointer> =
get_function!("user32.dll", EnableMouseInPointer);
}

#[derive(Clone)]
pub struct PointerTracker {
pointer_info: HashMap<PointerId, FullPointerState>,
Expand All @@ -494,7 +504,10 @@ impl PointerTracker {
pub fn new() -> PointerTracker {
PointerTracker {
pointer_info: HashMap::new(),
call_legacy_capture_fns: true,
call_legacy_capture_fns: match *ENABLE_MOUSE_IN_POINTER {
Some(EnableMouseInPointer) => unsafe{ EnableMouseInPointer(1) == 0 },
None => true,
},
}
}

Expand Down
64 changes: 52 additions & 12 deletions src/platform_impl/windows/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use winapi::{
use crate::{
dpi::{PhysicalDelta, PhysicalPosition, PhysicalSize, UnitlessDelta},
event::{
Event, Force, KeyEvent, LogicalKey, ModifiersState, PointerButton, PointerButtonEvent,
Event, Force, KeyEvent, LogicalKey, ModifiersState, PointerButton,
PointerId, RawKeyEvent, RawKeyboardEvent, RawPointerButtonEvent, RawPointerEvent,
WindowEvent, PointerTiltEvent,
},
Expand Down Expand Up @@ -1050,15 +1050,15 @@ unsafe extern "system" fn public_window_callback<T: 'static>(

subclass_input.send_event(Event::WindowEvent(
WindowId(window).into(),
WindowEvent::PointerScrollStarted(PointerId::MOUSE_ID),
WindowEvent::ScrollStarted,
));
subclass_input.send_event(Event::WindowEvent(
WindowId(window).into(),
WindowEvent::PointerScrollLines(PointerId::MOUSE_ID, UnitlessDelta::new(0.0, value)),
WindowEvent::ScrollLines(UnitlessDelta::new(0.0, value)),
));
subclass_input.send_event(Event::WindowEvent(
WindowId(window).into(),
WindowEvent::PointerScrollEnded(PointerId::MOUSE_ID),
WindowEvent::ScrollEnded,
));

0
Expand All @@ -1073,15 +1073,15 @@ unsafe extern "system" fn public_window_callback<T: 'static>(

subclass_input.send_event(Event::WindowEvent(
WindowId(window).into(),
WindowEvent::PointerScrollStarted(PointerId::MOUSE_ID),
WindowEvent::ScrollStarted,
));
subclass_input.send_event(Event::WindowEvent(
WindowId(window).into(),
WindowEvent::PointerScrollLines(PointerId::MOUSE_ID, UnitlessDelta::new(value, 0.0)),
WindowEvent::ScrollLines(UnitlessDelta::new(value, 0.0)),
));
subclass_input.send_event(Event::WindowEvent(
WindowId(window).into(),
WindowEvent::PointerScrollEnded(PointerId::MOUSE_ID),
WindowEvent::ScrollEnded,
));

0
Expand Down Expand Up @@ -1460,7 +1460,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(

let x = position.x as f64 + x.fract();
let y = position.y as f64 + y.fract();
let position = PhysicalPosition::new(x, y);
let mut position = PhysicalPosition::new(x, y);

let pi = std::f64::consts::PI;
let deg_to_twist = |deg: u32| (deg as f64 / 360.0) * pi;
Expand All @@ -1471,14 +1471,16 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
let pointer_id: PointerId;

let mut buttons_down = ButtonsDown::empty();
buttons_down.set(
PointerButton::TOUCH_CONTACT.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_INCONTACT != 0
);

match pointer_info.pointerType {
winuser::PT_TOUCH => {
pointer_id = PointerId::TouchId(TouchId(pointer_info.pointerId).into());

buttons_down.set(
PointerButton::TOUCH_CONTACT.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_INCONTACT != 0
);

let mut touch_info = mem::MaybeUninit::uninit();
if let Some(GetPointerTouchInfo) = *GET_POINTER_TOUCH_INFO {
let get_touch_info_result = GetPointerTouchInfo(
Expand All @@ -1502,6 +1504,12 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
}
winuser::PT_PEN => {
pointer_id = PointerId::PenId(PenId(pointer_info.pointerId).into());

buttons_down.set(
PointerButton::PEN_CONTACT.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_INCONTACT != 0
);

let mut pen_info = mem::MaybeUninit::uninit();
if let Some(GetPointerPenInfo) = *GET_POINTER_PEN_INFO {
let get_pen_info_result = GetPointerPenInfo(
Expand Down Expand Up @@ -1533,6 +1541,38 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
}
}
}
winuser::PT_MOUSE |
winuser::PT_TOUCHPAD => {
pointer_id = PointerId::MOUSE_ID;

// There can be some rounding errors when doing the mouse hiMetric math,
// and it doesn't make a whole lot of sense for a traditional mouse
// pointer to be on a non-integer point, so round off any fractional
// values that might've cropped up.
position.x = position.x.round();
position.y = position.y.round();
Comment on lines +1549 to +1553
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't make a whole lot of sense for a traditional mouse pointer to be on a non-integer point

I think this is a mistake. High-precision mouse pointers are widely supported and are useful for a number of purposes, e.g. in art applications. See prior discussion in #1375.

Copy link
Contributor Author

@Osspial Osspial Jun 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-precision mouse pointers are typically from pen/touch devices, which this rounding does not effect. If there are any relatively widely-used devices that this change would negatively effect I'd be open to reverting this, but otherwise I think it's an appropriate action to take for standard pixel-aligned mouse devices.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touchpads are a touch device. I'm not willing to assume without evidence that Windows always discards high-precision data from mice, either.

Copy link
Member

@kchibisov kchibisov Jun 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that touchpads are mouses actually? IIRC touchpads do have high precision data, but are treated as pointers on at least Wayland. So yeah, removing fractional part is likely not a good thing to do and I don't see much issue with it?

Also, even mouse on Wayland will have fractional part by default, so I'm not sure that things should diverge here. clients can always round themselves if they don't care, I guess.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, the terminology gets a bit confusing here. I mean that touchpads are high-precision input devices driven by a finger which can, should, and on non-Windows backends do have that precision propagated to the application. Even conventional mice can be, contrary to the comment above.

There's no reason a conventional mouse should be treated differently either, as you observe. Silently discarding perfectly good data is bad behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although it's not really useful in the windowing system with a standard pointer

I'd like to emphasize that high-precision pointers absolutely can be useful. For example, any time you're interacting with a vector or zoomed-out raster graphics canvas, or operating a continuous UI control like a slider.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or operating a continuous UI control like a slider

Ah, true there, I have actually noticed that on some KDE sliders where I can barely adjust them but the cursor doesn't actually move...

Copy link
Contributor Author

@Osspial Osspial Jun 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touchpads are a touch device. I'm not willing to assume without evidence that Windows always discards high-precision data from mice, either.

I have a laptop that uses the Windows Precision Touchpad drivers. When using the touchpad, Windows does not dispatch sub-pixel pointer movement events, and each movement event's position is within a +-0.05 margin of an integer pixel number. WRT mice - I have a Logitech G502 gaming mouse, and Windows exhibits the same behavior there. This is a rounding error, and should be treated as such.

My mouse, standard (very high resolution) mouse also sends fractional parts when moving, and although it's not really useful in the windowing system with a standard pointer, it's very useful in games that support them.

Are you on a Windows system? If so, could you check whether or not event-polish with this diff applied emits subpixel mouse movement events in the window example?

diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs
index 3702d352..b1d7ae8c 100644
--- a/src/platform_impl/windows/event_loop.rs
+++ b/src/platform_impl/windows/event_loop.rs
@@ -1549,8 +1549,8 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
                             // and it doesn't make a whole lot of sense for a traditional mouse
                             // pointer to be on a non-integer point, so round off any fractional
                             // values that might've cropped up.
-                            position.x = position.x.round();
-                            position.y = position.y.round();
+                            // position.x = position.x.round();
+                            // position.y = position.y.round();
 
                             buttons_down.set(
                                 PointerButton::MOUSE_LEFT.into_flags(),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is ultimately determined that windows genuinely never reports non-integer mouse/touchpad coordinates, then at the very least the comment should reflect that, rather than claiming there's no use case for higher precision data. That way if a future change makes higher-precision data available there won't be cause for confusion as to whether it should be discarded.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you on a Windows system?

I'm not, linux with a custom mouse driver because my mouse is... unique.


buttons_down.set(
PointerButton::MOUSE_LEFT.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_FIRSTBUTTON != 0,
);
buttons_down.set(
PointerButton::MOUSE_RIGHT.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_SECONDBUTTON != 0,
);
buttons_down.set(
PointerButton::MOUSE_MIDDLE.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_THIRDBUTTON != 0,
);
buttons_down.set(
PointerButton::MOUSE_X1.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_FOURTHBUTTON != 0,
);
buttons_down.set(
PointerButton::MOUSE_X2.into_flags(),
pointer_info.pointerFlags & winuser::POINTER_FLAG_FIFTHBUTTON != 0,
);
}
_ => return 0,
}

Expand Down