1 module gbaid.input;
2 
3 import derelict.sdl2.sdl;
4 
5 import gbaid.util;
6 
7 import gbaid.gba.keypad;
8 
9 public interface InputSource {
10     public void create();
11 
12     public void destroy();
13 
14     public void poll();
15 
16     @property public KeypadState keypadState();
17 
18     @property public bool quickSave();
19 
20     @property public uint lastDigit();
21 }
22 
23 public class Keyboard : InputSource {
24     private static enum int[10] DIGIT_CODES = [
25         SDL_SCANCODE_0, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4,
26         SDL_SCANCODE_5, SDL_SCANCODE_6, SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9
27     ];
28     private int[10] buttonCodeMap = [
29         SDL_SCANCODE_P,
30         SDL_SCANCODE_L,
31         SDL_SCANCODE_TAB,
32         SDL_SCANCODE_RETURN,
33         SDL_SCANCODE_D,
34         SDL_SCANCODE_A,
35         SDL_SCANCODE_W,
36         SDL_SCANCODE_S,
37         SDL_SCANCODE_RSHIFT,
38         SDL_SCANCODE_LSHIFT
39     ];
40     private int quickSaveKey = SDL_SCANCODE_Q;
41     private KeypadState state;
42     private bool save = false;
43     private uint digit = 0;
44 
45     public void map(Button button, int key) {
46         buttonCodeMap[button] = key;
47     }
48 
49     public override void create() {
50     }
51 
52     public override void destroy() {
53     }
54 
55     public override void poll() {
56         state.clear();
57         const ubyte* keyboard = SDL_GetKeyboardState(null);
58         foreach (buttonIndex, buttonCode; buttonCodeMap) {
59             state.setPressed(cast(Button) buttonIndex, cast(bool) keyboard[buttonCode]);
60         }
61         save = cast(bool) keyboard[quickSaveKey];
62         foreach (uint i, digitCode; DIGIT_CODES) {
63             if (cast(bool) keyboard[digitCode]) {
64                 digit = i;
65                 break;
66             }
67         }
68     }
69 
70     @property public override KeypadState keypadState() {
71         return state;
72     }
73 
74     @property public override bool quickSave() {
75         return save;
76     }
77 
78     @property public override uint lastDigit() {
79         return digit;
80     }
81 }
82 
83 public class Controller : InputSource {
84     private SDL_GameController* controller = null;
85     private int[10] buttonCodeMap = [
86         SDL_CONTROLLER_BUTTON_A,
87         SDL_CONTROLLER_BUTTON_B,
88         SDL_CONTROLLER_BUTTON_BACK,
89         SDL_CONTROLLER_BUTTON_START,
90         SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
91         SDL_CONTROLLER_BUTTON_DPAD_LEFT,
92         SDL_CONTROLLER_BUTTON_DPAD_UP,
93         SDL_CONTROLLER_BUTTON_DPAD_DOWN,
94         SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
95         SDL_CONTROLLER_BUTTON_LEFTSHOULDER
96     ];
97     private StickMapping[10] stickMap = [
98         StickMapping(),
99         StickMapping(),
100         StickMapping(),
101         StickMapping(),
102         StickMapping(SDL_CONTROLLER_AXIS_LEFTX, 0x4000, true),
103         StickMapping(SDL_CONTROLLER_AXIS_LEFTX, 0x4000, false),
104         StickMapping(SDL_CONTROLLER_AXIS_LEFTY, 0x4000, false),
105         StickMapping(SDL_CONTROLLER_AXIS_LEFTY, 0x4000, true),
106         StickMapping(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0x3000, true),
107         StickMapping(SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0x3000, true)
108     ];
109     private int quickSaveButton = SDL_CONTROLLER_BUTTON_X;
110     private KeypadState state;
111     private bool save = false;
112 
113     public void map(Button button, int controllerButton) {
114         buttonCodeMap[button] = controllerButton;
115     }
116 
117     public void map(Button button, int controllerAxis, float percent, bool direction) {
118         int threshold = cast(int) ((percent < 0 ? 0 : percent > 1 ? 1 : percent) * 0xFFFF - 0x8000);
119         stickMap[button] = StickMapping(controllerAxis, threshold, direction);
120     }
121 
122     public override void create() {
123         if (controller) {
124             return;
125         }
126         if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
127             if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) {
128                 throw new Exception("Failed to initialize the SDL controller system", toDString(SDL_GetError()));
129             }
130         }
131         foreach (i; 0 .. SDL_NumJoysticks()) {
132             if (SDL_IsGameController(i)) {
133                 controller = SDL_GameControllerOpen(i);
134                 if (!controller) {
135                     throw new Exception("Could not open controller", toDString(SDL_GetError()));
136                 }
137                 return;
138             }
139         }
140         throw new Exception("No controller found");
141     }
142 
143     public override void destroy() {
144         if (controller) {
145             SDL_GameControllerClose(controller);
146         }
147     }
148 
149     public override void poll() {
150         if (!controller) {
151             return;
152         }
153         state.clear();
154         foreach (buttonIndex, buttonCode; buttonCodeMap) {
155             if (buttonCode == SDL_CONTROLLER_BUTTON_INVALID) {
156                 continue;
157             }
158             state.setPressed(cast(Button) buttonIndex, cast(bool) SDL_GameControllerGetButton(controller, buttonCode));
159         }
160         foreach (buttonIndex, stick; stickMap) {
161             if (stick.axis == SDL_CONTROLLER_AXIS_INVALID) {
162                 continue;
163             }
164             auto amplitude = cast(int) SDL_GameControllerGetAxis(controller, stick.axis);
165             if ((stick.direction ? amplitude : -amplitude) >= stick.threshold) {
166                 state.setPressed(cast(Button) buttonIndex);
167             }
168         }
169         save = cast(bool) SDL_GameControllerGetButton(controller, quickSaveButton);
170     }
171 
172     @property public override KeypadState keypadState() {
173         return state;
174     }
175 
176     @property public override bool quickSave() {
177         return save;
178     }
179 
180     @property public override uint lastDigit() {
181         return 0;
182     }
183 
184     private static struct StickMapping {
185         private int axis = SDL_CONTROLLER_AXIS_INVALID;
186         private int threshold;
187         private bool direction;
188     }
189 }