@ -1,8 +1,12 @@
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using System.Linq ;
using System.Runtime.InteropServices ;
using Avalonia.Input ;
using Avalonia.Input ;
using Avalonia.Input.Raw ;
using Avalonia.Input.Raw ;
using Avalonia.Platform ;
using static Avalonia . X11 . XLib ;
using static Avalonia . X11 . XLib ;
namespace Avalonia.X11
namespace Avalonia.X11
@ -97,13 +101,15 @@ namespace Avalonia.X11
private AvaloniaX11Platform _ platform ;
private AvaloniaX11Platform _ platform ;
private XIValuatorClassInfo ? _ pressureXIValuatorClassInfo ;
private XIValuatorClassInfo ? _ pressureXIValuatorClassInfo ;
private XIValuatorClassInfo ? _ touchMajorXIValuatorClassInfo ;
private XIValuatorClassInfo ? _ touchMinorXIValuatorClassInfo ;
public bool Init ( AvaloniaX11Platform platform )
public bool Init ( AvaloniaX11Platform platform )
{
{
_ platform = platform ;
_ platform = platform ;
_ x11 = platform . Info ;
_ x11 = platform . Info ;
_ multitouch = platform . Options ? . EnableMultiTouch ? ? true ;
_ multitouch = platform . Options ? . EnableMultiTouch ? ? true ;
var devices = ( XIDeviceInfo * ) XIQueryDevice ( _ x11 . Display ,
var devices = ( XIDeviceInfo * ) XIQueryDevice ( _ x11 . Display ,
( int ) XiPredefinedDeviceId . XIAllMasterDevices , out int num ) ;
( int ) XiPredefinedDeviceId . XIAllMasterDevices , out int num ) ;
for ( var c = 0 ; c < num ; c + + )
for ( var c = 0 ; c < num ; c + + )
{
{
@ -118,8 +124,31 @@ namespace Avalonia.X11
if ( _ multitouch )
if ( _ multitouch )
{
{
// ABS_MT_TOUCH_MAJOR ABS_MT_TOUCH_MINOR
// https://www.kernel.org/doc/html/latest/input/multi-touch-protocol.html
var touchMajorAtom = XInternAtom ( _ x11 . Display , "Abs MT Touch Major" , false ) ;
var touchMinorAtom = XInternAtom ( _ x11 . Display , "Abs MT Touch Minor" , false ) ;
var pressureAtom = XInternAtom ( _ x11 . Display , "Abs MT Pressure" , false ) ;
var pressureAtom = XInternAtom ( _ x11 . Display , "Abs MT Pressure" , false ) ;
_ pressureXIValuatorClassInfo = _ pointerDevice . Valuators . FirstOrDefault ( t = > t . Label = = pressureAtom ) ;
var pressureXIValuatorClassInfo = _ pointerDevice . Valuators . FirstOrDefault ( t = > t . Label = = pressureAtom ) ;
if ( pressureXIValuatorClassInfo . Label = = pressureAtom )
{
// Why check twice? The XIValuatorClassInfo is struct, so the FirstOrDefault will return the default struct when not found.
_ pressureXIValuatorClassInfo = pressureXIValuatorClassInfo ;
}
var touchMajorXIValuatorClassInfo = _ pointerDevice . Valuators . FirstOrDefault ( t = > t . Label = = touchMajorAtom ) ;
if ( touchMajorXIValuatorClassInfo . Label = = touchMajorAtom )
{
_ touchMajorXIValuatorClassInfo = touchMajorXIValuatorClassInfo ;
}
var touchMinorXIValuatorClassInfo = _ pointerDevice . Valuators . FirstOrDefault ( t = > t . Label = = touchMinorAtom ) ;
if ( touchMinorXIValuatorClassInfo . Label = = touchMinorAtom )
{
_ touchMinorXIValuatorClassInfo = touchMinorXIValuatorClassInfo ;
}
}
}
/ *
/ *
@ -256,6 +285,57 @@ namespace Avalonia.X11
}
}
}
}
if ( _ touchMajorXIValuatorClassInfo is { } touchMajorXIValuatorClassInfo )
{
double? touchMajor = null ;
double? touchMinor = null ;
PixelRect screenBounds = default ;
if ( ev . Valuators . TryGetValue ( touchMajorXIValuatorClassInfo . Number , out var touchMajorValue ) )
{
var pixelPoint = new PixelPoint ( ( int ) ev . RootPosition . X , ( int ) ev . RootPosition . Y ) ;
var screen = _ platform . Screens . ScreenFromPoint ( pixelPoint ) ;
var screenBoundsFromPoint = screen ? . Bounds ;
Debug . Assert ( screenBoundsFromPoint ! = null ) ;
if ( screenBoundsFromPoint ! = null )
{
screenBounds = screenBoundsFromPoint . Value ;
// As https://www.kernel.org/doc/html/latest/input/multi-touch-protocol.html says, using `screenBounds.Width` is not accurate enough.
touchMajor = ( touchMajorValue - touchMajorXIValuatorClassInfo . Min ) /
( touchMajorXIValuatorClassInfo . Max - touchMajorXIValuatorClassInfo . Min ) * screenBounds . Width ;
}
}
if ( touchMajor ! = null )
{
if ( _ touchMinorXIValuatorClassInfo is { } touchMinorXIValuatorClassInfo )
{
if ( ev . Valuators . TryGetValue ( touchMinorXIValuatorClassInfo . Number , out var touchMinorValue ) )
{
touchMinor = ( touchMinorValue - touchMinorXIValuatorClassInfo . Min ) /
( touchMinorXIValuatorClassInfo . Max - touchMinorXIValuatorClassInfo . Min ) * screenBounds . Height ;
}
}
if ( touchMinor = = null )
{
touchMinor = touchMajor ;
}
var center = ev . Position ;
var leftX = center . X - touchMajor . Value / 2 ;
var topY = center . Y - touchMinor . Value / 2 ;
rawPointerPoint . ContactRect = new Rect
(
leftX ,
topY ,
touchMajor . Value ,
touchMinor . Value
) ;
}
}
client . ScheduleXI2Input ( new RawTouchEventArgs ( client . TouchDevice ,
client . ScheduleXI2Input ( new RawTouchEventArgs ( client . TouchDevice ,
ev . Timestamp , client . InputRoot , type , rawPointerPoint , ev . Modifiers , ev . Detail ) ) ;
ev . Timestamp , client . InputRoot , type , rawPointerPoint , ev . Modifiers , ev . Detail ) ) ;
return ;
return ;
@ -340,6 +420,7 @@ namespace Avalonia.X11
public RawInputModifiers Modifiers { get ; }
public RawInputModifiers Modifiers { get ; }
public ulong Timestamp { get ; }
public ulong Timestamp { get ; }
public Point Position { get ; }
public Point Position { get ; }
public Point RootPosition { get ; }
public int Button { get ; set ; }
public int Button { get ; set ; }
public int Detail { get ; set ; }
public int Detail { get ; set ; }
public bool Emulated { get ; set ; }
public bool Emulated { get ; set ; }
@ -385,6 +466,7 @@ namespace Avalonia.X11
Valuators = new Dictionary < int , double > ( ) ;
Valuators = new Dictionary < int , double > ( ) ;
Position = new Point ( ev - > event_x , ev - > event_y ) ;
Position = new Point ( ev - > event_x , ev - > event_y ) ;
RootPosition = new Point ( ev - > root_x , ev - > root_y ) ;
var values = ev - > valuators . Values ;
var values = ev - > valuators . Values ;
if ( ev - > valuators . Mask ! = null )
if ( ev - > valuators . Mask ! = null )
for ( var c = 0 ; c < ev - > valuators . MaskLen * 8 ; c + + )
for ( var c = 0 ; c < ev - > valuators . MaskLen * 8 ; c + + )