CPF/CPF.Linux/XI2Manager.cs
2023-11-21 23:05:03 +08:00

358 lines
15 KiB
C#

using CPF.Drawing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CPF.Input;
using CPF.Controls;
using static CPF.Linux.XLib;
namespace CPF.Linux
{
public unsafe class XI2Manager
{
private static readonly XiEventType[] DefaultEventTypes = new XiEventType[]
{
XiEventType.XI_Motion,
XiEventType.XI_ButtonPress,
XiEventType.XI_ButtonRelease
};
private static readonly XiEventType[] MultiTouchEventTypes = new XiEventType[]
{
XiEventType.XI_TouchBegin,
XiEventType.XI_TouchUpdate,
XiEventType.XI_TouchEnd
};
private X11Info _x11;
private bool _multitouch;
//private Dictionary<IntPtr, IXI2Client> _clients = new Dictionary<IntPtr, IXI2Client>();
class DeviceInfo
{
public int Id { get; }
public XIValuatorClassInfo[] Valuators { get; private set; }
public XIScrollClassInfo[] Scrollers { get; private set; }
public DeviceInfo(XIDeviceInfo info)
{
Id = info.Deviceid;
Update(info.Classes, info.NumClasses);
}
public virtual void Update(XIAnyClassInfo** classes, int num)
{
var valuators = new List<XIValuatorClassInfo>();
var scrollers = new List<XIScrollClassInfo>();
for (var c = 0; c < num; c++)
{
if (classes[c]->Type == XiDeviceClass.XIValuatorClass)
valuators.Add(*((XIValuatorClassInfo**)classes)[c]);
if (classes[c]->Type == XiDeviceClass.XIScrollClass)
scrollers.Add(*((XIScrollClassInfo**)classes)[c]);
}
Valuators = valuators.ToArray();
Scrollers = scrollers.ToArray();
}
public void UpdateValuators(Dictionary<int, double> valuators)
{
foreach (var v in valuators)
{
if (Valuators.Length > v.Key)
Valuators[v.Key].Value = v.Value;
}
}
}
class PointerDeviceInfo : DeviceInfo
{
public PointerDeviceInfo(XIDeviceInfo info) : base(info)
{
}
public bool HasScroll(ParsedDeviceEvent ev)
{
foreach (var val in ev.Valuators)
if (Scrollers.Any(s => s.Number == val.Key))
return true;
return false;
}
public bool HasMotion(ParsedDeviceEvent ev)
{
foreach (var val in ev.Valuators)
if (Scrollers.All(s => s.Number != val.Key))
return true;
return false;
}
}
private PointerDeviceInfo _pointerDevice;
private LinuxPlatform _platform;
public bool Init(LinuxPlatform platform)
{
_platform = platform;
_x11 = platform.Info;
_multitouch = true;
var devices = (XIDeviceInfo*)XIQueryDevice(_x11.Display,
(int)XiPredefinedDeviceId.XIAllMasterDevices, out int num);
for (var c = 0; c < num; c++)
{
if (devices[c].Use == XiDeviceType.XIMasterPointer)
{
_pointerDevice = new PointerDeviceInfo(devices[c]);
break;
}
}
if (_pointerDevice == null)
return false;
/*
int mask = 0;
XISetMask(ref mask, XiEventType.XI_DeviceChanged);
var emask = new XIEventMask
{
Mask = &mask,
Deviceid = _pointerDevice.Id,
MaskLen = XiEventMaskLen
};
if (XISelectEvents(_x11.Display, _x11.RootWindow, &emask, 1) != Status.Success)
return false;
return true;
*/
return XiSelectEvents(_x11.Display, _x11.RootWindow, new Dictionary<int, List<XiEventType>>
{
[_pointerDevice.Id] = new List<XiEventType>
{
XiEventType.XI_DeviceChanged
}
}) == Status.Success;
}
public void AddWindow(IntPtr handle)
{
//_clients[xid] = window;
//System.Threading.Thread.Sleep(5000);
var eventsLength = DefaultEventTypes.Length;
if (_multitouch)
eventsLength += MultiTouchEventTypes.Length;
var events = new List<XiEventType>(eventsLength);
events.AddRange(DefaultEventTypes);
if (_multitouch)
events.AddRange(MultiTouchEventTypes);
var s = XiSelectEvents(_x11.Display, handle, new Dictionary<int, List<XiEventType>> { [_pointerDevice.Id] = events });
//Console.WriteLine(s);
//// We are taking over mouse input handling from here
//return XEventMask.PointerMotionMask
// | XEventMask.ButtonMotionMask
// | XEventMask.Button1MotionMask
// | XEventMask.Button2MotionMask
// | XEventMask.Button3MotionMask
// | XEventMask.Button4MotionMask
// | XEventMask.Button5MotionMask
// | XEventMask.ButtonPressMask
// | XEventMask.ButtonReleaseMask;
}
//public void OnWindowDestroyed(IntPtr xid) => _clients.Remove(xid);
public void OnEvent(XIEvent* xev, X11Window client)
{
if (xev->evtype == XiEventType.XI_DeviceChanged)
{
var changed = (XIDeviceChangedEvent*)xev;
_pointerDevice.Update(changed->Classes, changed->NumClasses);
}
if ((xev->evtype >= XiEventType.XI_ButtonPress && xev->evtype <= XiEventType.XI_Motion)
|| (xev->evtype >= XiEventType.XI_TouchBegin && xev->evtype <= XiEventType.XI_TouchEnd))
{
var dev = (XIDeviceEvent*)xev;
//if (_clients.TryGetValue(dev->EventWindow, out var client))
OnDeviceEvent(client, new ParsedDeviceEvent(dev));
}
}
void OnDeviceEvent(X11Window client, ParsedDeviceEvent ev)
{
//Console.WriteLine(ev.Type + " Emulated:" + ev.Emulated);
if (ev.Type == XiEventType.XI_TouchBegin
|| ev.Type == XiEventType.XI_TouchUpdate
|| ev.Type == XiEventType.XI_TouchEnd)
{
var type = ev.Type == XiEventType.XI_TouchBegin ?
EventType.TouchDown :
(ev.Type == XiEventType.XI_TouchUpdate ?
EventType.TouchMove :
EventType.TouchUp);
client.root.InputManager.TouchDevice.ProcessEvent(new TouchEventArgs(new TouchPoint { Id = ev.Detail, Position = ev.Position / client.RenderScaling }, client.root.InputManager.TouchDevice, client.root), client.root.LayoutManager.VisibleUIElements, type);
//client.ScheduleInput(new RawTouchEventArgs(client.TouchDevice, ev.Timestamp, client.InputRoot, type, ev.Position, ev.Modifiers, ev.Detail));
return;
}
//if (_multitouch && ev.Emulated)
// return;
if (ev.Type == XiEventType.XI_Motion)
{
Vector scrollDelta = default;
foreach (var v in ev.Valuators)
{
foreach (var scroller in _pointerDevice.Scrollers)
{
if (scroller.Number == v.Key)
{
var old = _pointerDevice.Valuators[scroller.Number].Value;
// Value was zero after reset, ignore the event and use it as a reference next time
if (old == 0)
continue;
var diff = (old - v.Value) / scroller.Increment;
if (scroller.ScrollType == XiScrollType.Horizontal)
scrollDelta = scrollDelta.WithX(scrollDelta.X + (float)diff);
else
scrollDelta = scrollDelta.WithY(scrollDelta.Y + (float)diff);
}
}
}
if (scrollDelta != default)
{ //client.ScheduleInput(new MouseWheelEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, ev.Position, scrollDelta, ev.Modifiers));
//client.root.InputManager.MouseDevice.ProcessEvent(new MouseWheelEventArgs(client.root, ev.Modifiers.HasFlag(InputModifiers.LeftMouseButton), ev.Modifiers.HasFlag(InputModifiers.RightMouseButton), ev.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), ev.Position / client.LayoutScaling, client.root.InputManager.MouseDevice, scrollDelta), client.root.LayoutManager.VisibleUIElements, EventType.MouseWheel);
client.MouseEvent(EventType.MouseWheel, ev.Position, ev.Modifiers, scrollDelta, MouseButton.None);
}
if (_pointerDevice.HasMotion(ev))
{
//client.ScheduleInput(new MouseEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, RawPointerEventType.Move, ev.Position, ev.Modifiers));
//client.root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(client.root, ev.Modifiers.HasFlag(InputModifiers.LeftMouseButton), ev.Modifiers.HasFlag(InputModifiers.RightMouseButton), ev.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), ev.Position / client.LayoutScaling, client.root.InputManager.MouseDevice, ev.Emulated), client.root.LayoutManager.VisibleUIElements, EventType.MouseMove);
client.MouseEvent(EventType.MouseMove, ev.Position, ev.Modifiers, new Vector(), MouseButton.None, ev.Emulated);
}
}
if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease)
{
if (!client.ActivateTransientChildIfNeeded())
{
var down = ev.Type == XiEventType.XI_ButtonPress;
//var type = ev.Button switch
//{
// 1 => down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp,
// 2 => down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp,
// 3 => down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp,
// 8 => down ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Up,
// 9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up,
// _ => (RawPointerEventType?)null
//};
//if (type.HasValue)
// client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
// type.Value, ev.Position, ev.Modifiers));
MouseButton? mouseButton = null;
switch (ev.Button)
{
case 1:
mouseButton = MouseButton.Left;
break;
case 2:
mouseButton = MouseButton.Middle;
break;
case 3:
mouseButton = MouseButton.Right;
break;
case 8:
mouseButton = MouseButton.XButton1;
break;
case 9:
mouseButton = MouseButton.XButton2;
break;
}
if (mouseButton != null)
{
//client.root.InputManager.MouseDevice.ProcessEvent(new MouseButtonEventArgs(client.root, ev.Modifiers.HasFlag(InputModifiers.LeftMouseButton), ev.Modifiers.HasFlag(InputModifiers.RightMouseButton), ev.Modifiers.HasFlag(InputModifiers.MiddleMouseButton), ev.Position / client.LayoutScaling, client.root.InputManager.MouseDevice, mouseButton.Value, ev.Emulated), client.root.LayoutManager.VisibleUIElements, down ? EventType.MouseDown : EventType.MouseUp);
client.MouseEvent(down ? EventType.MouseDown : EventType.MouseUp, ev.Position, ev.Modifiers, new Vector(), mouseButton.Value, ev.Emulated);
}
}
}
_pointerDevice.UpdateValuators(ev.Valuators);
}
}
unsafe class ParsedDeviceEvent
{
public XiEventType Type { get; }
public InputModifiers Modifiers { get; }
public ulong Timestamp { get; }
public Point Position { get; }
public int Button { get; set; }
public int Detail { get; set; }
public bool Emulated { get; set; }
public Dictionary<int, double> Valuators { get; }
public ParsedDeviceEvent(XIDeviceEvent* ev)
{
Type = ev->evtype;
Timestamp = (ulong)ev->time.ToInt64();
var state = (XModifierMask)ev->mods.Effective;
if (state.HasFlag(XModifierMask.ShiftMask))
Modifiers |= InputModifiers.Shift;
if (state.HasFlag(XModifierMask.ControlMask))
Modifiers |= InputModifiers.Control;
if (state.HasFlag(XModifierMask.Mod1Mask))
Modifiers |= InputModifiers.Alt;
//if (state.HasFlag(XModifierMask.Mod4Mask))
// Modifiers |= InputModifiers.Meta;
if (ev->buttons.MaskLen > 0)
{
var buttons = ev->buttons.Mask;
if (XIMaskIsSet(buttons, 1))
Modifiers |= InputModifiers.LeftMouseButton;
if (XIMaskIsSet(buttons, 2))
Modifiers |= InputModifiers.MiddleMouseButton;
if (XIMaskIsSet(buttons, 3))
Modifiers |= InputModifiers.RightMouseButton;
//if (XIMaskIsSet(buttons, 8))
// Modifiers |= InputModifiers.XButton1MouseButton;
//if (XIMaskIsSet(buttons, 9))
// Modifiers |= InputModifiers.XButton2MouseButton;
}
Valuators = new Dictionary<int, double>();
Position = new Point((float)ev->event_x, (float)ev->event_y);
var values = ev->valuators.Values;
for (var c = 0; c < ev->valuators.MaskLen * 8; c++)
if (XIMaskIsSet(ev->valuators.Mask, c))
Valuators[c] = *values++;
if (Type == XiEventType.XI_ButtonPress || Type == XiEventType.XI_ButtonRelease)
Button = ev->detail;
Detail = ev->detail;
Emulated = ev->flags.HasFlag(XiDeviceEventFlags.XIPointerEmulated);
}
}
//interface IXI2Client
//{
// View InputRoot { get; }
// void ScheduleInput(InputEventArgs args);
// MouseDevice MouseDevice { get; }
// TouchDevice TouchDevice { get; }
//}
}