A game framework written with osu! in mind.
at master 175 lines 6.7 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 4#if NET5_0 5using OpenTabletDriver.Plugin; 6using OpenTabletDriver.Plugin.Output; 7using OpenTabletDriver.Plugin.Platform.Pointer; 8using OpenTabletDriver.Plugin.Tablet; 9using osu.Framework.Bindables; 10using osu.Framework.Input.StateChanges; 11using osu.Framework.Platform; 12using osu.Framework.Statistics; 13using osuTK; 14 15namespace osu.Framework.Input.Handlers.Tablet 16{ 17 public class OpenTabletDriverHandler : InputHandler, IAbsolutePointer, IVirtualTablet, IRelativePointer, ITabletHandler 18 { 19 public override bool IsActive => tabletDriver.EnableInput; 20 21 private TabletDriver tabletDriver; 22 23 public Bindable<Vector2> AreaOffset { get; } = new Bindable<Vector2>(); 24 25 public Bindable<Vector2> AreaSize { get; } = new Bindable<Vector2>(); 26 27 public Bindable<float> Rotation { get; } = new Bindable<float>(); 28 29 public IBindable<TabletInfo> Tablet => tablet; 30 31 private readonly Bindable<TabletInfo> tablet = new Bindable<TabletInfo>(); 32 33 public override bool Initialize(GameHost host) 34 { 35 tabletDriver = new TabletDriver 36 { 37 // for now let's keep things simple and always use absolute mode. 38 // this will likely be a user setting in the future. 39 OutputMode = new AbsoluteTabletMode(this) 40 }; 41 42 updateOutputArea(host.Window); 43 44 host.Window.Resized += () => updateOutputArea(host.Window); 45 46 AreaOffset.BindValueChanged(_ => updateInputArea()); 47 AreaSize.BindValueChanged(_ => updateInputArea(), true); 48 Rotation.BindValueChanged(_ => updateInputArea(), true); 49 50 tabletDriver.TabletChanged += (sender, e) => updateInputArea(); 51 tabletDriver.ReportReceived += (sender, report) => 52 { 53 switch (report) 54 { 55 case ITabletReport tabletReport: 56 handleTabletReport(tabletReport); 57 break; 58 59 case IAuxReport auxiliaryReport: 60 handleAuxiliaryReport(auxiliaryReport); 61 break; 62 } 63 }; 64 65 Enabled.BindValueChanged(d => 66 { 67 if (d.NewValue) 68 { 69 if (tabletDriver.Tablet == null) 70 tabletDriver.DetectTablet(); 71 } 72 73 tabletDriver.EnableInput = d.NewValue; 74 }, true); 75 76 return true; 77 } 78 79 void IAbsolutePointer.SetPosition(System.Numerics.Vector2 pos) => enqueueInput(new MousePositionAbsoluteInput { Position = new Vector2(pos.X, pos.Y) }); 80 81 void IVirtualTablet.SetPressure(float percentage) => enqueueInput(new MouseButtonInput(osuTK.Input.MouseButton.Left, percentage > 0)); 82 83 void IRelativePointer.Translate(System.Numerics.Vector2 delta) => enqueueInput(new MousePositionRelativeInput { Delta = new Vector2(delta.X, delta.Y) }); 84 85 private void updateOutputArea(IWindow window) 86 { 87 switch (tabletDriver.OutputMode) 88 { 89 case AbsoluteOutputMode absoluteOutputMode: 90 { 91 float outputWidth, outputHeight; 92 93 // Set output area in pixels 94 absoluteOutputMode.Output = new Area 95 { 96 Width = outputWidth = window.ClientSize.Width, 97 Height = outputHeight = window.ClientSize.Height, 98 Position = new System.Numerics.Vector2(outputWidth / 2, outputHeight / 2) 99 }; 100 break; 101 } 102 } 103 } 104 105 private void updateInputArea() 106 { 107 if (tabletDriver.Tablet == null) 108 { 109 tablet.Value = null; 110 return; 111 } 112 113 float inputWidth = tabletDriver.Tablet.Digitizer.Width; 114 float inputHeight = tabletDriver.Tablet.Digitizer.Height; 115 116 AreaSize.Default = new Vector2(inputWidth, inputHeight); 117 118 // if it's clear the user has not configured the area, take the full area from the tablet that was just found. 119 if (AreaSize.Value == Vector2.Zero) 120 AreaSize.SetDefault(); 121 122 AreaOffset.Default = new Vector2(inputWidth / 2, inputHeight / 2); 123 124 // likewise with the position, use the centre point if it has not been configured. 125 // it's safe to assume no user would set their centre point to 0,0 for now. 126 if (AreaOffset.Value == Vector2.Zero) 127 AreaOffset.SetDefault(); 128 129 tablet.Value = new TabletInfo(tabletDriver.Tablet.TabletProperties.Name, AreaSize.Default); 130 131 switch (tabletDriver.OutputMode) 132 { 133 case AbsoluteOutputMode absoluteOutputMode: 134 { 135 // Set input area in millimeters 136 absoluteOutputMode.Input = new Area 137 { 138 Width = AreaSize.Value.X, 139 Height = AreaSize.Value.Y, 140 Position = new System.Numerics.Vector2(AreaOffset.Value.X, AreaOffset.Value.Y), 141 Rotation = Rotation.Value 142 }; 143 break; 144 } 145 } 146 } 147 148 private void handleTabletReport(ITabletReport tabletReport) 149 { 150 int buttonCount = tabletReport.PenButtons.Length; 151 var buttons = new ButtonInputEntry<TabletPenButton>[buttonCount]; 152 for (int i = 0; i < buttonCount; i++) 153 buttons[i] = new ButtonInputEntry<TabletPenButton>((TabletPenButton)i, tabletReport.PenButtons[i]); 154 155 enqueueInput(new TabletPenButtonInput(buttons)); 156 } 157 158 private void handleAuxiliaryReport(IAuxReport auxiliaryReport) 159 { 160 int buttonCount = auxiliaryReport.AuxButtons.Length; 161 var buttons = new ButtonInputEntry<TabletAuxiliaryButton>[buttonCount]; 162 for (int i = 0; i < buttonCount; i++) 163 buttons[i] = new ButtonInputEntry<TabletAuxiliaryButton>((TabletAuxiliaryButton)i, auxiliaryReport.AuxButtons[i]); 164 165 enqueueInput(new TabletAuxiliaryButtonInput(buttons)); 166 } 167 168 private void enqueueInput(IInput input) 169 { 170 PendingInputs.Enqueue(input); 171 FrameStatistics.Increment(StatisticsCounterType.TabletEvents); 172 } 173 } 174} 175#endif