mirror of
				https://gitee.com/csharpui/CPF.git
				synced 2025-11-01 00:46:56 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			358 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			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; }
 | |
|     //}
 | |
| }
 | 
