A game framework written with osu! in mind.
at master 124 lines 4.8 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System; 5using System.Drawing; 6using osu.Framework.Extensions.EnumExtensions; 7using osu.Framework.Input.Handlers.Mouse; 8using osu.Framework.Input.StateChanges; 9using osu.Framework.Platform.Windows.Native; 10using osuTK; 11using SDL2; 12 13// ReSharper disable UnusedParameter.Local (Class regularly handles native events where we don't consume all parameters) 14 15namespace osu.Framework.Platform.Windows 16{ 17 /// <summary> 18 /// A windows specific mouse input handler which overrides the SDL2 implementation of raw input. 19 /// This is done to better handle quirks of some devices. 20 /// </summary> 21 internal unsafe class WindowsMouseHandler : MouseHandler 22 { 23 private const int raw_input_coordinate_space = 65535; 24 25 private SDL.SDL_WindowsMessageHook callback; 26 private SDL2DesktopWindow window; 27 28 public override bool IsActive => Enabled.Value; 29 30 public override bool Initialize(GameHost host) 31 { 32 if (!(host.Window is SDL2DesktopWindow desktopWindow)) 33 return false; 34 35 window = desktopWindow; 36 callback = (ptr, wnd, u, param, l) => onWndProc(ptr, wnd, u, param, l); 37 38 Enabled.BindValueChanged(enabled => 39 { 40 host.InputThread.Scheduler.Add(() => SDL.SDL_SetWindowsMessageHook(enabled.NewValue ? callback : null, IntPtr.Zero)); 41 }, true); 42 43 return base.Initialize(host); 44 } 45 46 protected override void HandleMouseMoveRelative(Vector2 delta) 47 { 48 // handled via custom logic below. 49 } 50 51 private IntPtr onWndProc(IntPtr userData, IntPtr hWnd, uint message, ulong wParam, long lParam) 52 { 53 if (!Enabled.Value) 54 return IntPtr.Zero; 55 56 if (message != Native.Input.WM_INPUT) 57 return IntPtr.Zero; 58 59 int payloadSize = sizeof(RawInputData); 60 61 Native.Input.GetRawInputData((IntPtr)lParam, RawInputCommand.Input, out var data, ref payloadSize, sizeof(RawInputHeader)); 62 63 if (data.Header.Type != RawInputType.Mouse) 64 return IntPtr.Zero; 65 66 var mouse = data.Mouse; 67 68 //TODO: this isn't correct. 69 if (mouse.ExtraInformation > 0) 70 { 71 // i'm not sure if there is a valid case where we need to handle packets with this present 72 // but the osu!tablet fires noise events with non-zero values, which we want to ignore. 73 // return IntPtr.Zero; 74 } 75 76 var position = new Vector2(mouse.LastX, mouse.LastY); 77 float sensitivity = (float)Sensitivity.Value; 78 79 if (mouse.Flags.HasFlagFast(RawMouseFlags.MoveAbsolute)) 80 { 81 var screenRect = mouse.Flags.HasFlagFast(RawMouseFlags.VirtualDesktop) ? Native.Input.VirtualScreenRect : new Rectangle(window.Position, window.ClientSize); 82 83 Vector2 screenSize = new Vector2(screenRect.Width, screenRect.Height); 84 85 if (mouse.LastX == 0 && mouse.LastY == 0) 86 { 87 // not sure if this is the case for all tablets, but on osu!tablet these can appear and are noise. 88 return IntPtr.Zero; 89 } 90 91 // i am not sure what this 64 flag is, but it's set on the osu!tablet at very least. 92 // using it here as a method of determining where the coordinate space is incorrect. 93 if (((int)mouse.Flags & 64) == 0) 94 { 95 position /= raw_input_coordinate_space; 96 position *= screenSize; 97 } 98 99 if (Sensitivity.Value != 1) 100 { 101 // apply absolute sensitivity adjustment from the centre of the screen area. 102 Vector2 halfScreenSize = (screenSize / 2); 103 104 position -= halfScreenSize; 105 position *= (float)Sensitivity.Value; 106 position += halfScreenSize; 107 } 108 109 // map from screen to client coordinate space. 110 // not using Window's PointToClient implementation to keep floating point precision here. 111 position -= new Vector2(window.Position.X, window.Position.Y); 112 position *= window.Scale; 113 114 PendingInputs.Enqueue(new MousePositionAbsoluteInput { Position = position }); 115 } 116 else 117 { 118 PendingInputs.Enqueue(new MousePositionRelativeInput { Delta = new Vector2(mouse.LastX, mouse.LastY) * sensitivity }); 119 } 120 121 return IntPtr.Zero; 122 } 123 } 124}