[OpenTK] Introduce OpenTK (#336)
[platform/core/csapi/tizenfx.git] / external / src / OpenTK / OpenTK / Platform / SDL2 / Sdl2JoystickDriver.cs
1 //
2 // The Open Toolkit Library License
3 //
4 // Copyright (c) 2006 - 2013 Stefanos Apostolopoulos for the Open Toolkit library.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights to
9 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 // the Software, and to permit persons to whom the Software is furnished to do
11 // so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in all
14 // copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 // OTHER DEALINGS IN THE SOFTWARE.
24 //
25
26 using System;
27 using System.Collections.Generic;
28 using System.Diagnostics;
29 using OpenTK.Input;
30
31 namespace OpenTK.Platform.SDL2
32 {
33     internal class Sdl2JoystickDriver : IJoystickDriver2, IGamePadDriver, IDisposable
34     {
35         private const float RangeMultiplier =  1.0f / 32768.0f;
36         private readonly MappedGamePadDriver gamepad_driver = new MappedGamePadDriver();
37         private bool disposed;
38
39         private class Sdl2JoystickDetails
40         {
41             public IntPtr Handle { get; set; }
42             public Guid Guid { get; set; }
43             public int InstanceId { get; set; }
44             public int PacketNumber { get; set; }
45             public int HatCount { get; set; }
46             public int BallCount { get; set; }
47             public bool IsConnected { get; set; }
48             public readonly JoystickHatState[] Hat =
49                 new JoystickHatState[JoystickState.MaxHats];
50         }
51
52         // For IJoystickDriver2 implementation
53         private readonly List<JoystickDevice> joysticks = new List<JoystickDevice>(4);
54
55         private readonly Dictionary<int, int> sdl_instanceid_to_joysticks = new Dictionary<int, int>();
56
57 #if USE_SDL2_GAMECONTROLLER
58         class Sdl2GamePad
59         {
60             public IntPtr Handle { get; private set; }
61             public GamePadState State;
62             public GamePadCapabilities Capabilities;
63
64             public Sdl2GamePad(IntPtr handle)
65             {
66                 Handle = handle;
67             }
68         }
69
70         int last_controllers_instance = 0;
71         readonly List<Sdl2GamePad> controllers = new List<Sdl2GamePad>(4);
72         readonly Dictionary<int, int> sdl_instanceid_to_controllers = new Dictionary<int, int>();
73 #endif
74
75         public Sdl2JoystickDriver()
76         {
77         }
78
79         private JoystickDevice<Sdl2JoystickDetails> OpenJoystick(int id)
80         {
81             JoystickDevice<Sdl2JoystickDetails> joystick = null;
82             int num_axes = 0;
83             int num_buttons = 0;
84             int num_hats = 0;
85             int num_balls = 0;
86
87             IntPtr handle = SDL.JoystickOpen(id);
88             if (handle != IntPtr.Zero)
89             {
90                 num_axes = SDL.JoystickNumAxes(handle);
91                 num_buttons = SDL.JoystickNumButtons(handle);
92                 num_hats = SDL.JoystickNumHats(handle);
93                 num_balls = SDL.JoystickNumBalls(handle);
94
95                 joystick = new JoystickDevice<Sdl2JoystickDetails>(id, num_axes, num_buttons);
96                 joystick.Description = SDL.JoystickName(handle);
97                 joystick.Details.Handle = handle;
98                 joystick.Details.InstanceId = SDL.JoystickInstanceID(handle);
99                 joystick.Details.Guid = SDL.JoystickGetGUID(handle).ToGuid();
100                 joystick.Details.HatCount = num_hats;
101                 joystick.Details.BallCount = num_balls;
102
103                 Debug.Print("[SDL2] Joystick device {0} opened successfully. ", id);
104                 Debug.Print("\t\t'{0}' has {1} axes, {2} buttons, {3} hats, {4} balls",
105                     joystick.Description, joystick.Axis.Count, joystick.Button.Count,
106                     joystick.Details.HatCount, joystick.Details.BallCount);
107             }
108             else
109             {
110                 Debug.Print("[SDL2] Failed to open joystick device {0}", id);
111             }
112
113             return joystick;
114         }
115
116         private bool IsJoystickValid(int id)
117         {
118             return id >= 0 && id < joysticks.Count;
119         }
120
121         private bool IsJoystickInstanceValid(int instance_id)
122         {
123             return sdl_instanceid_to_joysticks.ContainsKey(instance_id);
124         }
125
126         private OpenTK.Input.HatPosition TranslateHat(HatPosition value)
127         {
128             if ((value & HatPosition.LeftUp) == HatPosition.LeftUp)
129             {
130                 return OpenTK.Input.HatPosition.UpLeft;
131             }
132
133             if ((value & HatPosition.RightUp) == HatPosition.RightUp)
134             {
135                 return OpenTK.Input.HatPosition.UpRight;
136             }
137
138             if ((value & HatPosition.LeftDown) == HatPosition.LeftDown)
139             {
140                 return OpenTK.Input.HatPosition.DownLeft;
141             }
142
143             if ((value & HatPosition.RightDown) == HatPosition.RightDown)
144             {
145                 return OpenTK.Input.HatPosition.DownRight;
146             }
147
148             if ((value & HatPosition.Up) == HatPosition.Up)
149             {
150                 return OpenTK.Input.HatPosition.Up;
151             }
152
153             if ((value & HatPosition.Right) == HatPosition.Right)
154             {
155                 return OpenTK.Input.HatPosition.Right;
156             }
157
158             if ((value & HatPosition.Down) == HatPosition.Down)
159             {
160                 return OpenTK.Input.HatPosition.Down;
161             }
162
163             if ((value & HatPosition.Left) == HatPosition.Left)
164             {
165                 return OpenTK.Input.HatPosition.Left;
166             }
167
168             return OpenTK.Input.HatPosition.Centered;
169         }
170
171 #if USE_SDL2_GAMECONTROLLER
172         bool IsControllerValid(int id)
173         {
174             return id >= 0 && id < controllers.Count;
175         }
176
177         bool IsControllerInstanceValid(int instance_id)
178         {
179             return sdl_instanceid_to_controllers.ContainsKey(instance_id);
180         }
181
182         GamePadAxes GetBoundAxes(IntPtr gamecontroller)
183         {
184             GamePadAxes axes = 0;
185             axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftX) ? GamePadAxes.LeftX : 0;
186             axes |= IsAxisBind(gamecontroller, GameControllerAxis.LeftY) ? GamePadAxes.LeftY : 0;
187             axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightX) ? GamePadAxes.RightX : 0;
188             axes |= IsAxisBind(gamecontroller, GameControllerAxis.RightY) ? GamePadAxes.RightY : 0;
189             axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerLeft) ? GamePadAxes.LeftTrigger : 0;
190             axes |= IsAxisBind(gamecontroller, GameControllerAxis.TriggerRight) ? GamePadAxes.RightTrigger : 0;
191             return axes;
192         }
193
194         Buttons GetBoundButtons(IntPtr gamecontroller)
195         {
196             Buttons buttons = 0;
197             buttons |= IsButtonBind(gamecontroller, GameControllerButton.A) ? Buttons.A : 0;
198             buttons |= IsButtonBind(gamecontroller, GameControllerButton.B) ? Buttons.B : 0;
199             buttons |= IsButtonBind(gamecontroller, GameControllerButton.X) ? Buttons.X : 0;
200             buttons |= IsButtonBind(gamecontroller, GameControllerButton.Y) ? Buttons.Y : 0;
201             buttons |= IsButtonBind(gamecontroller, GameControllerButton.START) ? Buttons.Start : 0;
202             buttons |= IsButtonBind(gamecontroller, GameControllerButton.BACK) ? Buttons.Back : 0;
203             buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSHOULDER) ? Buttons.LeftShoulder : 0;
204             buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSHOULDER) ? Buttons.RightShoulder : 0;
205             buttons |= IsButtonBind(gamecontroller, GameControllerButton.LEFTSTICK) ? Buttons.LeftStick : 0;
206             buttons |= IsButtonBind(gamecontroller, GameControllerButton.RIGHTSTICK) ? Buttons.RightStick : 0;
207             buttons |= IsButtonBind(gamecontroller, GameControllerButton.GUIDE) ? Buttons.BigButton : 0;
208             buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_DOWN) ? Buttons.DPadDown : 0;
209             buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_UP) ? Buttons.DPadUp : 0;
210             buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_LEFT) ? Buttons.DPadLeft : 0;
211             buttons |= IsButtonBind(gamecontroller, GameControllerButton.DPAD_RIGHT) ? Buttons.DPadRight : 0;
212             return buttons;
213         }
214
215         bool IsAxisBind(IntPtr gamecontroller, GameControllerAxis axis)
216         {
217             GameControllerButtonBind bind =
218                 SDL.GameControllerGetBindForAxis(gamecontroller, axis);
219             return bind.BindType == GameControllerBindType.Axis;
220         }
221
222         bool IsButtonBind(IntPtr gamecontroller, GameControllerButton button)
223         {
224             GameControllerButtonBind bind =
225                 SDL.GameControllerGetBindForButton(gamecontroller, button);
226             return bind.BindType == GameControllerBindType.Button;
227         }
228
229         GamePadAxes TranslateAxis(GameControllerAxis axis)
230         {
231             switch (axis)
232             {
233                 case GameControllerAxis.LeftX:
234                     return GamePadAxes.LeftX;
235
236                 case GameControllerAxis.LeftY:
237                     return GamePadAxes.LeftY;
238
239                 case GameControllerAxis.RightX:
240                     return GamePadAxes.RightX;
241
242                 case GameControllerAxis.RightY:
243                     return GamePadAxes.RightY;
244
245                 case GameControllerAxis.TriggerLeft:
246                     return GamePadAxes.LeftTrigger;
247
248                 case GameControllerAxis.TriggerRight:
249                     return GamePadAxes.RightTrigger;
250
251                 default:
252                     throw new ArgumentOutOfRangeException(
253                         String.Format("[SDL] Unknown axis {0}", axis));
254             }
255         }
256
257         Buttons TranslateButton(GameControllerButton button)
258         {
259             switch (button)
260             {
261                 case GameControllerButton.A:
262                     return Buttons.A;
263
264                 case GameControllerButton.B:
265                     return Buttons.B;
266
267                 case  GameControllerButton.X:
268                     return Buttons.X;
269
270                 case GameControllerButton.Y:
271                     return Buttons.Y;
272
273                 case GameControllerButton.LEFTSHOULDER:
274                     return Buttons.LeftShoulder;
275
276                 case GameControllerButton.RIGHTSHOULDER:
277                     return Buttons.RightShoulder;
278
279                 case GameControllerButton.LEFTSTICK:
280                     return Buttons.LeftStick;
281
282                 case GameControllerButton.RIGHTSTICK:
283                     return Buttons.RightStick;
284
285                 case GameControllerButton.DPAD_UP:
286                     return Buttons.DPadUp;
287
288                 case GameControllerButton.DPAD_DOWN:
289                     return Buttons.DPadDown;
290
291                 case GameControllerButton.DPAD_LEFT:
292                     return Buttons.DPadLeft;
293
294                 case GameControllerButton.DPAD_RIGHT:
295                     return Buttons.DPadRight;
296
297                 case GameControllerButton.BACK:
298                     return Buttons.Back;
299
300                 case GameControllerButton.START:
301                     return Buttons.Start;
302
303                 case GameControllerButton.GUIDE:
304                     return Buttons.BigButton;
305
306                 default:
307                     Debug.Print("[SDL2] Unknown button {0}", button);
308                     return 0;
309             }
310         }
311 #endif
312
313         public void ProcessJoystickEvent(JoyDeviceEvent ev)
314         {
315             int id = ev.Which;
316             if (id < 0)
317             {
318                 Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
319                 return;
320             }
321
322             switch (ev.Type)
323             {
324                 case EventType.JOYDEVICEADDED:
325                     {
326                         IntPtr handle = SDL.JoystickOpen(id);
327                         if (handle != IntPtr.Zero)
328                         {
329                             JoystickDevice<Sdl2JoystickDetails> joystick = OpenJoystick(id);
330
331                             int instance_id = joystick.Details.InstanceId;
332                             int device_id = id;
333
334                             if (joystick != null)
335                             {
336                                 joystick.Details.IsConnected = true;
337                                 if (device_id < joysticks.Count)
338                                 {
339                                     joysticks[device_id] = joystick;
340                                 }
341                                 else
342                                 {
343                                     joysticks.Add(joystick);
344                                 }
345
346                                 sdl_instanceid_to_joysticks.Add(instance_id, device_id);
347                             }
348                         }
349                     }
350                     break;
351
352                 case EventType.JOYDEVICEREMOVED:
353                     if (IsJoystickInstanceValid(id))
354                     {
355                         int instance_id = id;
356                         int device_id = sdl_instanceid_to_joysticks[instance_id];
357
358                         JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[device_id];
359                         joystick.Details.IsConnected = false;
360
361                         sdl_instanceid_to_joysticks.Remove(instance_id);
362                     }
363                     else
364                     {
365                         Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
366                     }
367                     break;
368             }
369         }
370
371         public void ProcessJoystickEvent(JoyAxisEvent ev)
372         {
373             int id = ev.Which;
374             if (IsJoystickInstanceValid(id))
375             {
376                 int index = sdl_instanceid_to_joysticks[id];
377                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
378                 float value = ev.Value * RangeMultiplier;
379                 joystick.SetAxis(ev.Axis, value);
380                 joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
381             }
382             else
383             {
384                 Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
385             }
386         }
387
388         public void ProcessJoystickEvent(JoyBallEvent ev)
389         {
390             int id = ev.Which;
391             if (IsJoystickInstanceValid(id))
392             {
393                 int index = sdl_instanceid_to_joysticks[id];
394                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
395                 // Todo: does it make sense to support balls?
396                 joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
397             }
398             else
399             {
400                 Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
401             }
402         }
403
404         public void ProcessJoystickEvent(JoyButtonEvent ev)
405         {
406             int id = ev.Which;
407             if (IsJoystickInstanceValid(id))
408             {
409                 int index = sdl_instanceid_to_joysticks[id];
410                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
411                 joystick.SetButton(ev.Button, ev.State == State.Pressed);
412                 joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
413             }
414             else
415             {
416                 Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
417             }
418         }
419
420         public void ProcessJoystickEvent(JoyHatEvent ev)
421         {
422             int id = ev.Which;
423             if (IsJoystickInstanceValid(id))
424             {
425                 int index = sdl_instanceid_to_joysticks[id];
426                 JoystickDevice<Sdl2JoystickDetails> joystick = (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
427                 if (ev.Hat >= 0 && ev.Hat < JoystickState.MaxHats)
428                 {
429                     joystick.Details.Hat[ev.Hat] = new JoystickHatState(TranslateHat(ev.Value));
430                 }
431                 else
432                 {
433                     Debug.Print("[SDL2] Hat {0} out of range [0, {1}]", ev.Hat, JoystickState.MaxHats);
434                 }
435                 joystick.Details.PacketNumber = Math.Max(0, unchecked(joystick.Details.PacketNumber + 1));
436             }
437             else
438             {
439                 Debug.Print("[SDL2] Invalid joystick id {0} in {1}", id, ev.Type);
440             }
441         }
442
443 #if USE_SDL2_GAMECONTROLLER
444         public void ProcessControllerEvent(ControllerDeviceEvent ev)
445         {
446             int id = ev.Which;
447             if (id < 0)
448             {
449                 Debug.Print("[SDL2] Invalid controller id {0} in {1}", id, ev.Type);
450                 return;
451             }
452
453             switch (ev.Type)
454             {
455                 case EventType.CONTROLLERDEVICEADDED:
456                     IntPtr handle = SDL.GameControllerOpen(id);
457                     if (handle != IntPtr.Zero)
458                     {
459                         // The id variable here corresponds to a device_id between 0 and Sdl.NumJoysticks().
460                         // It is only used in the ADDED event. All other events use an instance_id which increases
461                         // monotonically in each ADDED event.
462                         // The idea is that device_id refers to the n-th connected joystick, whereas instance_id
463                         // refers to the actual hardware device behind the n-th joystick.
464                         // Yes, it's confusing.
465                         int device_id = id;
466                         int instance_id = last_controllers_instance++;
467
468                         Sdl2GamePad pad = new Sdl2GamePad(handle);
469
470                         IntPtr joystick = SDL.GameControllerGetJoystick(handle);
471                         if (joystick != IntPtr.Zero)
472                         {
473                             pad.Capabilities = new GamePadCapabilities(
474                                 GamePadType.GamePad,
475                                 GetBoundAxes(joystick),
476                                 GetBoundButtons(joystick),
477                                 true);
478                             pad.State.SetConnected(true);
479
480                             // Connect this device and add the relevant device index
481                             if (controllers.Count <= id)
482                             {
483                                 controllers.Add(pad);
484                             }
485                             else
486                             {
487                                 controllers[device_id] = pad;
488                             }
489
490                             sdl_instanceid_to_controllers.Add(instance_id, device_id);
491                         }
492                         else
493                         {
494                             Debug.Print("[SDL2] Failed to retrieve joystick from game controller. Error: {0}", SDL.GetError());
495                         }
496                     }
497                     break;
498
499                 case EventType.CONTROLLERDEVICEREMOVED:
500                     if (IsControllerInstanceValid(id))
501                     {
502                         int instance_id = id;
503                         int device_id = sdl_instanceid_to_controllers[instance_id];
504
505                         controllers[device_id].State.SetConnected(false);
506                         sdl_instanceid_to_controllers.Remove(device_id);
507                     }
508                     else
509                     {
510                         Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type);
511                     }
512                     break;
513
514                 case EventType.CONTROLLERDEVICEREMAPPED:
515                     if (IsControllerInstanceValid(id))
516                     {
517                         // Todo: what should we do in this case?
518                     }
519                     else
520                     {
521                         Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", id, ev.Type);
522                     }
523                     break;
524             }
525          }
526
527         public void ProcessControllerEvent(ControllerAxisEvent ev)
528         {
529             int instance_id = ev.Which;
530             if (IsControllerInstanceValid(instance_id))
531             {
532                 int id = sdl_instanceid_to_controllers[instance_id];
533                 controllers[id].State.SetAxis(TranslateAxis(ev.Axis), ev.Value);
534             }
535             else
536             {
537                 Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type);
538             }
539         }
540
541         public void ProcessControllerEvent(ControllerButtonEvent ev)
542         {
543             int instance_id = ev.Which;
544             if (IsControllerInstanceValid(instance_id))
545             {
546                 int id = sdl_instanceid_to_controllers[instance_id];
547                 controllers[id].State.SetButton(TranslateButton(ev.Button), ev.State == State.Pressed);
548             }
549             else
550             {
551                 Debug.Print("[SDL2] Invalid game controller instance {0} in {1}", instance_id, ev.Type);
552             }
553         }
554 #endif
555
556 #if USE_SDL2_GAMECONTOLLER
557         public GamePadCapabilities GetCapabilities(int index)
558         {
559             if (IsControllerValid(index))
560             {
561                 return controllers[index].Capabilities;
562             }
563             return new GamePadCapabilities();
564         }
565
566         public GamePadState GetState(int index)
567         {
568             if (IsControllerValid(index))
569             {
570                 return controllers[index].State;
571             }
572             return new GamePadState();
573         }
574
575         public string GetName(int index)
576         {
577             return String.Empty;
578         }
579 #else
580         public GamePadCapabilities GetCapabilities(int index)
581         {
582             return gamepad_driver.GetCapabilities(index);
583         }
584
585         public GamePadState GetState(int index)
586         {
587             return gamepad_driver.GetState(index);
588         }
589
590         public string GetName(int index)
591         {
592             return gamepad_driver.GetName(index);
593         }
594
595         public bool SetVibration(int index, float left, float right)
596         {
597             return false;
598         }
599 #endif
600
601         JoystickState IJoystickDriver2.GetState(int index)
602         {
603             JoystickState state = new JoystickState();
604             if (IsJoystickValid(index))
605             {
606                 JoystickDevice<Sdl2JoystickDetails> joystick =
607                     (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
608
609                 for (int i = 0; i < joystick.Axis.Count; i++)
610                 {
611                     state.SetAxis(i, (short)(joystick.Axis[i] * short.MaxValue + 0.5f));
612                 }
613
614                 for (int i = 0; i < joystick.Button.Count; i++)
615                 {
616                     state.SetButton(i, joystick.Button[i]);
617                 }
618
619                 for (int i = 0; i < joystick.Details.HatCount; i++)
620                 {
621                     state.SetHat(JoystickHat.Hat0 + i, joystick.Details.Hat[i]);
622                 }
623
624                 state.SetIsConnected(joystick.Details.IsConnected);
625                 state.SetPacketNumber(joystick.Details.PacketNumber);
626             }
627
628             return state;
629         }
630
631         JoystickCapabilities IJoystickDriver2.GetCapabilities(int index)
632         {
633             if (IsJoystickValid(index))
634             {
635                 JoystickDevice<Sdl2JoystickDetails> joystick =
636                     (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
637
638                 return new JoystickCapabilities(
639                     joystick.Axis.Count,
640                     joystick.Button.Count,
641                     joystick.Details.HatCount,
642                     joystick.Details.IsConnected);
643             }
644             return new JoystickCapabilities();
645         }
646
647         Guid IJoystickDriver2.GetGuid(int index)
648         {
649             Guid guid = new Guid();
650             if (IsJoystickValid(index))
651             {
652                 JoystickDevice<Sdl2JoystickDetails> joystick =
653                     (JoystickDevice<Sdl2JoystickDetails>)joysticks[index];
654
655                 return joystick.Details.Guid;
656             }
657             return guid;
658         }
659
660         private void Dispose(bool manual)
661         {
662             if (!disposed)
663             {
664                 if (manual)
665                 {
666                     Debug.Print("Disposing {0}", GetType());
667
668                     foreach (var j in joysticks)
669                     {
670                         var joystick = (JoystickDevice<Sdl2JoystickDetails>)j;
671                         IntPtr handle = joystick.Details.Handle;
672                         SDL.JoystickClose(handle);
673                     }
674
675                     joysticks.Clear();
676                 }
677                 else
678                 {
679                     Debug.Print("{0} leaked, did you forget to call Dispose()?", GetType());
680                 }
681                 disposed = true;
682             }
683         }
684
685         public void Dispose()
686         {
687             Dispose(true);
688             GC.SuppressFinalize(this);
689         }
690
691         ~Sdl2JoystickDriver()
692         {
693             Dispose(false);
694         }
695     }
696 }
697