2 // MappedGamePadDriver.cs
5 // Stefanos A. <stapostol@gmail.com>
7 // Copyright (c) 2006-2013 Stefanos Apostolopoulos
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 using System.Collections.Generic;
32 namespace OpenTK.Platform
36 /// Implements IGamePadDriver using OpenTK.Input.Joystick
37 /// and a gamepad-specific axis/button mapping.
41 /// This class supports OpenTK and is not meant to be accessed by user code.
44 /// To support gamepads on platforms that do not offer a gamepad-optimized API,
45 /// we need to use the generic OpenTK.Input.Joystick and implement a custom
46 /// mapping scheme to provide a stable mapping to OpenTK.Input.GamePad. This
47 /// class implements this mapping scheme.
50 internal class MappedGamePadDriver : IGamePadDriver
52 private readonly GamePadConfigurationDatabase database =
53 new GamePadConfigurationDatabase();
55 private readonly Dictionary<Guid, GamePadConfiguration> configurations =
56 new Dictionary<Guid, GamePadConfiguration>();
58 public GamePadState GetState(int index)
60 JoystickState joy = Joystick.GetState(index);
61 GamePadState pad = new GamePadState();
65 pad.SetConnected(true);
66 pad.SetPacketNumber(joy.PacketNumber);
68 GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index));
70 foreach (GamePadConfigurationItem map in configuration)
72 switch (map.Source.Type)
74 case ConfigurationType.Axis:
76 // JoystickAxis -> Buttons/GamePadAxes mapping
77 int source_axis = map.Source.Axis;
78 short value = joy.GetAxisRaw(source_axis);
80 switch (map.Target.Type)
82 case ConfigurationType.Axis:
83 pad.SetAxis(map.Target.Axis, value);
86 case ConfigurationType.Button:
87 // Todo: if SDL2 GameController config is ever updated to
88 // distinguish between negative/positive axes, then remove
90 // Button is considered press when the axis is >= 0.5 from center
91 pad.SetButton(map.Target.Button, Math.Abs(value) >= short.MaxValue >> 1);
97 case ConfigurationType.Button:
99 // JoystickButton -> Buttons/GamePadAxes mapping
100 int source_button = map.Source.Button;
101 bool pressed = joy.GetButton(source_button) == ButtonState.Pressed;
103 switch (map.Target.Type)
105 case ConfigurationType.Axis:
106 // Todo: if SDL2 GameController config is ever updated to
107 // distinguish between negative/positive axes, then update
108 // the following line to support both.
109 short value = pressed ?
111 (map.Target.Axis & (GamePadAxes.LeftTrigger | GamePadAxes.RightTrigger)) != 0 ?
114 pad.SetAxis(map.Target.Axis, value);
117 case ConfigurationType.Button:
118 pad.SetButton(map.Target.Button, pressed);
124 case ConfigurationType.Hat:
126 // JoystickHat -> Buttons/GamePadAxes mapping
127 JoystickHat source_hat = map.Source.Hat;
128 JoystickHatState state = joy.GetHat(source_hat);
130 bool pressed = false;
131 switch (map.Source.HatPosition)
133 case HatPosition.Down:
134 pressed = state.IsDown;
138 pressed = state.IsUp;
141 case HatPosition.Left:
142 pressed = state.IsLeft;
145 case HatPosition.Right:
146 pressed = state.IsRight;
150 switch (map.Target.Type)
152 case ConfigurationType.Axis:
153 // Todo: if SDL2 GameController config is ever updated to
154 // distinguish between negative/positive axes, then update
155 // the following line to support both.
156 short value = pressed ?
158 (map.Target.Axis & (GamePadAxes.LeftTrigger | GamePadAxes.RightTrigger)) != 0 ?
161 pad.SetAxis(map.Target.Axis, value);
164 case ConfigurationType.Button:
165 pad.SetButton(map.Target.Button, pressed);
177 public GamePadCapabilities GetCapabilities(int index)
179 JoystickCapabilities joy = Joystick.GetCapabilities(index);
180 GamePadCapabilities pad;
183 GamePadConfiguration configuration = GetConfiguration(Joystick.GetGuid(index));
184 GamePadAxes mapped_axes = 0;
185 Buttons mapped_buttons = 0;
187 foreach (GamePadConfigurationItem map in configuration)
189 switch (map.Target.Type)
191 case ConfigurationType.Axis:
192 mapped_axes |= map.Target.Axis;
195 case ConfigurationType.Button:
196 mapped_buttons |= map.Target.Button;
201 pad = new GamePadCapabilities(
202 GamePadType.GamePad, // Todo: detect different types
206 configuration.Name != GamePadConfigurationDatabase.UnmappedName);
210 pad = new GamePadCapabilities();
215 public string GetName(int index)
217 JoystickCapabilities joy = Joystick.GetCapabilities(index);
218 string name = String.Empty;
221 GamePadConfiguration map = GetConfiguration(Joystick.GetGuid(index));
227 public bool SetVibration(int index, float left, float right)
232 private GamePadConfiguration GetConfiguration(Guid guid)
234 if (!configurations.ContainsKey(guid))
236 string config = database[guid];
237 GamePadConfiguration map = new GamePadConfiguration(config);
238 configurations.Add(guid, map);
240 return configurations[guid];
243 private bool IsMapped(GamePadConfigurationSource item)
245 return item.Type != ConfigurationType.Unmapped;