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

feat: implement the X Input extension to support touch input, smooth scrolling, etc. #15799

Merged
merged 15 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ private void UseDevice(PointerPoint pointer, Gdk.Device device)
properties.IsRightButtonPressed = IsPressed(state, ModifierType.Button3Mask, properties.PointerUpdateKind, RightButtonPressed, RightButtonReleased);
properties.IsXButton1Pressed = IsPressed(state, ModifierType.Button4Mask, properties.PointerUpdateKind, XButton1Pressed, XButton1Released);
properties.IsXButton2Pressed = IsPressed(state, ModifierType.Button5Mask, properties.PointerUpdateKind, XButton1Pressed, XButton2Released);
properties.IsTouchPad = dev.Source == InputSource.Touchpad;
break;

case PointerDeviceType.Pen:
Expand Down
141 changes: 141 additions & 0 deletions src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.CoreProtocol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System;
using Windows.Devices.Input;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Input;
using Microsoft.UI.Xaml.Controls;
using Uno.UI.Hosting;

namespace Uno.WinUI.Runtime.Skia.X11;

internal partial class X11PointerInputSource
{
private const int LEFT = 1;
private const int MIDDLE = 2;
private const int RIGHT = 3;
private const int SCROLL_UP = 4;
private const int SCROLL_DOWN = 5;
private const int SCROLL_LEFT = 6;
private const int SCROLL_RIGHT = 7;
private const int XButton1 = 8;
private const int XButton2 = 9;

private Point _mousePosition;
private int _pressedButtons; // // bit 0 is not used

public void ProcessLeaveEvent(XCrossingEvent ev)
{
_mousePosition = new Point(ev.x, ev.y);

var point = CreatePointFromCurrentState(ev.time);
var modifiers = X11XamlRootHost.XModifierMaskToVirtualKeyModifiers(ev.state);

var args = new PointerEventArgs(point, modifiers);

CreatePointFromCurrentState(ev.time);
X11XamlRootHost.QueueAction(_host, () => RaisePointerExited(args));
}

public void ProcessEnterEvent(XCrossingEvent ev)
{
_mousePosition = new Point(ev.x, ev.y);

var args = CreatePointerEventArgsFromCurrentState(ev.time, ev.state);
X11XamlRootHost.QueueAction(_host, () => RaisePointerEntered(args));
}

public void ProcessMotionNotifyEvent(XMotionEvent ev)
{
_mousePosition = new Point(ev.x, ev.y);

var args = CreatePointerEventArgsFromCurrentState(ev.time, ev.state);
X11XamlRootHost.QueueAction(_host, () => RaisePointerMoved(args));
}

public void ProcessButtonPressedEvent(XButtonEvent ev)
{
_mousePosition = new Point(ev.x, ev.y);
_pressedButtons = (byte)(_pressedButtons | 1 << ev.button);

var args = CreatePointerEventArgsFromCurrentState(ev.time, ev.state);

if (ev.button is SCROLL_LEFT or SCROLL_RIGHT or SCROLL_UP or SCROLL_DOWN)
{
// These scrolling events are shown as a ButtonPressed with a corresponding ButtonReleased in succession.
// We arbitrarily choose to handle this on the Pressed side and ignore the Released side.
// Note that this makes scrolling discrete, i.e. there is no Scrolling delta. Instead, we get a separate
// Pressed/Released pair for each scroll wheel "detent".

var props = args.CurrentPoint.Properties;
props.IsHorizontalMouseWheel = ev.button is SCROLL_LEFT or SCROLL_RIGHT;
props.MouseWheelDelta = ev.button is SCROLL_LEFT or SCROLL_UP ?
ScrollContentPresenter.ScrollViewerDefaultMouseWheelDelta :
-ScrollContentPresenter.ScrollViewerDefaultMouseWheelDelta;

X11XamlRootHost.QueueAction(_host, () => RaisePointerWheelChanged(args));
}
else
{
X11XamlRootHost.QueueAction(_host, () => RaisePointerPressed(args));
}
}

// Note about removing devices: the server emits a ButtonRelease if a device is removed
// while a button is held.
public void ProcessButtonReleasedEvent(XButtonEvent ev)
{
// TODO: what if button released when not same_screen?
if (ev.button is SCROLL_LEFT or SCROLL_RIGHT or SCROLL_UP or SCROLL_DOWN)
{
// Scroll events are already handled in ProcessButtonPressedEvent
return;
}

_mousePosition = new Point(ev.x, ev.y);
_pressedButtons = (byte)(_pressedButtons & ~(1 << ev.button));

var args = CreatePointerEventArgsFromCurrentState(ev.time, ev.state);
X11XamlRootHost.QueueAction(_host, () => RaisePointerReleased(args));
}

private PointerEventArgs CreatePointerEventArgsFromCurrentState(IntPtr time, XModifierMask state)
{
var point = CreatePointFromCurrentState(time);
var modifiers = X11XamlRootHost.XModifierMaskToVirtualKeyModifiers(state);

return new PointerEventArgs(point, modifiers);
}

/// <summary>
/// Create a new PointerPoint from the current state of the PointerInputSource
/// </summary>
private PointerPoint CreatePointFromCurrentState(IntPtr time)
{
var properties = new PointerPointProperties
{
// TODO: fill this comprehensively
IsLeftButtonPressed = (_pressedButtons & (1 << LEFT)) != 0,
IsMiddleButtonPressed = (_pressedButtons & (1 << MIDDLE)) != 0,
IsRightButtonPressed = (_pressedButtons & (1 << RIGHT)) != 0
};

var scale = ((IXamlRootHost)_host).RootElement?.XamlRoot is { } root
? root.RasterizationScale
: 1;

var point = new PointerPoint(
frameId: (uint)time, // UNO TODO: How should set the frame, timestamp may overflow.
timestamp: (uint)(time * TimeSpan.TicksPerMillisecond), // Time is given in milliseconds since system boot. See also: https://github.com/unoplatform/uno/issues/14535
PointerDevice.For(PointerDeviceType.Mouse),
0, // TODO: XInput
new Point(_mousePosition.X / scale, _mousePosition.Y / scale),
new Point(_mousePosition.X / scale, _mousePosition.Y / scale),
properties.HasPressedButton,
properties.SetUpdateKindFromPrevious(_previousPointerPointProperties)
);

_previousPointerPointProperties = properties;

return point;
}
}
73 changes: 0 additions & 73 deletions src/Uno.UI.Runtime.Skia.X11/X11PointerInputSource.Mouse.cs

This file was deleted.

Loading
Loading