diablo controls dialog
[drnoksnes] / platform / sdli.cpp
1 #include <SDL.h>
2 #include <math.h>
3
4 #include "platform.h"
5 #include "snes9x.h"
6 #include "display.h"
7 #include "sdlv.h" // Dispatching video-related events
8
9 struct TouchButton {
10         unsigned short mask;
11         unsigned short x, y;
12         unsigned short x2, y2;
13         double fx, fy;
14         double fw, fh;
15 };
16
17 #define TOUCH_BUTTON_INITIALIZER(name, x, y, w, h) \
18         {SNES_##name##_MASK, 0, 0, 0, 0, x, y, w, h}
19
20 #define kCornerButtonWidth      (0.375)
21 #define kCornerButtonHeight     (0.0833333333334)
22 #define kBigButtonWidth         (0.125)
23 #define kBigButtonHeight        (0.2777777777778)
24
25 static TouchButton touchbuttons[] = {
26         TOUCH_BUTTON_INITIALIZER(TL, 0.0, 0.0, kCornerButtonWidth, kCornerButtonHeight),
27         TOUCH_BUTTON_INITIALIZER(TR, 0.625, 0.0, kCornerButtonWidth, kCornerButtonHeight),
28         TOUCH_BUTTON_INITIALIZER(UP, kBigButtonWidth, kCornerButtonHeight, kBigButtonWidth, kBigButtonHeight),
29         TOUCH_BUTTON_INITIALIZER(LEFT, 0.0, kCornerButtonHeight + kBigButtonHeight, kBigButtonWidth, kBigButtonHeight),
30         TOUCH_BUTTON_INITIALIZER(RIGHT, 2.0 * kBigButtonWidth, kCornerButtonHeight + kBigButtonHeight, kBigButtonWidth, kBigButtonHeight),
31         TOUCH_BUTTON_INITIALIZER(DOWN, kBigButtonWidth, 1.0 - (kCornerButtonHeight + kBigButtonHeight), kBigButtonWidth, kBigButtonHeight),
32         TOUCH_BUTTON_INITIALIZER(SELECT, 0.0, 1.0 - kCornerButtonHeight, kCornerButtonWidth, kCornerButtonHeight),
33         TOUCH_BUTTON_INITIALIZER(X, 1.0 - 2.0 * kBigButtonWidth, kCornerButtonHeight, kBigButtonWidth, kBigButtonHeight),
34         TOUCH_BUTTON_INITIALIZER(Y, 1.0 - 3.0 * kBigButtonWidth, kCornerButtonHeight + kBigButtonHeight, kBigButtonWidth, kBigButtonHeight),
35         TOUCH_BUTTON_INITIALIZER(A, 1.0 - kBigButtonWidth, kCornerButtonHeight + kBigButtonHeight, kBigButtonWidth, kBigButtonHeight),
36         TOUCH_BUTTON_INITIALIZER(B, 1.0 - 2.0 * kBigButtonWidth, 1.0 - (kCornerButtonHeight + kBigButtonHeight), kBigButtonWidth, kBigButtonHeight),
37         TOUCH_BUTTON_INITIALIZER(START, 1.0 - kCornerButtonWidth, 1.0 - kCornerButtonHeight, kCornerButtonWidth, kCornerButtonHeight),
38 };
39
40 static TouchButton* current = 0;
41
42 static uint32 joypads[2];
43 static struct {
44         unsigned x;
45         unsigned y;
46         bool enabled, pressed;
47 } mouse;
48
49 static TouchButton* getButtonFor(unsigned int x, unsigned int y) {
50         unsigned int i;
51
52         for (i = 0; i < sizeof(touchbuttons)/sizeof(TouchButton); i++) {
53                 if (x >= touchbuttons[i].x && x < touchbuttons[i].x2 &&
54                         y >= touchbuttons[i].y && y < touchbuttons[i].y2) {
55
56                         return &touchbuttons[i];
57                 }
58         }
59
60         return 0;
61 }
62
63 static inline void unpress(TouchButton* b) {
64         joypads[0] &= ~b->mask;
65 }
66 static inline void press(TouchButton* b) {
67         joypads[0] |= b->mask;
68 }
69
70 static void processMouse(unsigned int x, unsigned int y, int pressed = 0)
71 {
72 #if CONF_EXIT_BUTTON
73         /* no fullscreen escape button, we have to simulate one! */
74         /* TODO: don't hardcode sizes */
75         if (Config.fullscreen && x > (800 - 100) && y < 50 && pressed > 0) {
76                 S9xDoAction(kActionQuit);
77         }
78 #endif
79         if (Config.touchscreenInput) {
80                 if (pressed < 0) {
81                         // Button up.
82                         if (current) {
83                                 // Leaving button
84                                 unpress(current);
85                                 current = 0;
86                         }
87                 } else {
88                         // Button down, or mouse motion.
89                         TouchButton* b = getButtonFor(x, y);
90                         if (current && b && current != b) {
91                                 // Moving from button to button
92                                 unpress(current);
93                                 current = b;
94                                 press(current);
95                         } else if (current && !b) {
96                                 // Leaving button
97                                 unpress(current);
98                                 current = 0;
99                         } else if (!current && b) {
100                                 // Entering button
101                                 current = b;
102                                 press(current);
103                         }
104                 }
105         } else if (mouse.enabled) {
106                 mouse.x = x;
107                 mouse.y = y;
108
109                 if (mouse.x < GUI.RenderX) mouse.x = 0;
110                 else {
111                         mouse.x -= GUI.RenderX;
112                         if (mouse.x > GUI.RenderW) mouse.x = GUI.RenderW;
113                 }
114
115                 if (mouse.y < GUI.RenderY) mouse.y = 0;
116                 else {
117                         mouse.y -= GUI.RenderY;
118                         if (mouse.y > GUI.RenderH) mouse.y = GUI.RenderH;
119                 }
120
121                 // mouse.{x,y} are system coordinates.
122                 // Scale them to emulated screen coordinates.
123                 mouse.x = static_cast<unsigned int>(mouse.x / GUI.ScaleX);
124                 mouse.y = static_cast<unsigned int>(mouse.y / GUI.ScaleY);
125
126                 if (pressed > 0)
127                         mouse.pressed = true;
128                 else if (pressed < 0)
129                         mouse.pressed = false;
130         }
131 }
132
133 static void processEvent(const SDL_Event& event)
134 {
135         switch (event.type) 
136         {
137                 case SDL_KEYDOWN:
138                         if (Config.action[event.key.keysym.scancode]) 
139                                 S9xDoAction(Config.action[event.key.keysym.scancode]);
140                         joypads[0] |= Config.joypad1Mapping[event.key.keysym.scancode];
141                         joypads[1] |= Config.joypad2Mapping[event.key.keysym.scancode];
142                         break;
143                 case SDL_KEYUP:
144                         joypads[0] &= ~Config.joypad1Mapping[event.key.keysym.scancode];
145                         joypads[1] &= ~Config.joypad2Mapping[event.key.keysym.scancode];
146                         break;
147                 case SDL_MOUSEBUTTONUP:
148                 case SDL_MOUSEBUTTONDOWN:
149                         processMouse(event.button.x, event.button.y,
150                                         (event.button.state == SDL_PRESSED) ? 1 : - 1);
151                         break;
152                 case SDL_MOUSEMOTION:
153                         processMouse(event.motion.x, event.motion.y);
154                         break;
155                 case SDL_QUIT:
156                         Config.quitting = true;
157                         break;
158                 case SDL_ACTIVEEVENT:
159                 case SDL_SYSWMEVENT:
160                         processVideoEvent(event);
161                         break;
162         }
163 }
164
165 /** This function is called to return a bit-wise mask of the state of one of the
166         five emulated SNES controllers.
167
168         @return 0 if you're not supporting controllers past a certain number or
169                 return the mask representing the current state of the controller number
170                 passed as a parameter or'ed with 0x80000000.
171 */
172
173 uint32 S9xReadJoypad (int which)
174 {
175         if (which < 0 || which >= 2) {
176                 // More joypads that we currently handle (could happen if bad conf)
177                 return 0;
178         }
179
180         return joypads[which];
181 }
182
183 /** Get the current position of the host pointing device, usually a mouse,
184         used to emulated the SNES mouse.
185
186         @param buttons The buttons return value is a bit-wise mask of the two SNES
187                 mouse buttons, bit 0 for button 1 (left) and bit 1 for button 2 (right).
188 */
189 bool8 S9xReadMousePosition(int which1, int& x, int& y, uint32& buttons)
190 {
191         if (which1 != 0) return FALSE;
192
193         x = mouse.x;
194         y = mouse.y;
195         buttons = mouse.pressed ? 1 : 0;
196
197         return TRUE;
198 }
199
200 bool8 S9xReadSuperScopePosition(int& x, int& y, uint32& buttons)
201 {
202         x = mouse.x;
203         y = mouse.y;
204         buttons = mouse.pressed ? 8 : 0;
205
206         return TRUE;
207 }
208
209 /** Get and process system/input events.
210         @param block true to block, false to poll until the queue is empty.
211 */
212 void S9xProcessEvents(bool block)
213 {
214         SDL_Event event;
215
216         if (block) {
217                 SDL_WaitEvent(&event);
218                 processEvent(event);
219         } else {
220                 while(SDL_PollEvent(&event)) {
221                         processEvent(event);
222                 }
223         }
224 }
225
226 void S9xInitInputDevices()
227 {
228         joypads[0] = 0;
229         joypads[1] = 0;
230         mouse.enabled = false;
231         mouse.pressed = false;
232
233         if (Config.joypad1Enabled) {
234                 joypads[0] = 0x80000000UL;
235         }
236         if (Config.joypad2Enabled) {
237                 joypads[1] = 0x80000000UL;
238         }
239
240         // Pretty print some information
241         printf("Input: ");
242         if (Config.joypad1Enabled) {
243                 printf("Player 1 (joypad)");
244                 if (Config.joypad2Enabled) {
245                         printf("+ player 2 (joypad)");
246                 }
247         } else if (Config.joypad2Enabled) {
248                 printf("Player 2 (joypad)");
249         } else {
250                 printf("Nothing");
251         }
252         printf("\n");
253
254         // TODO Non-awful mouse support, Superscope
255
256         S9xInputScreenChanged();
257 }
258
259 void S9xDeinitInputDevices()
260 {
261         joypads[0] = 0;
262         joypads[1] = 0;
263         mouse.enabled = false;
264         mouse.pressed = false;
265 }
266
267 void S9xInputScreenChanged()
268 {
269         unsigned int i = 0;
270         const unsigned int w = GUI.Width, h = GUI.Height;
271         for (i = 0; i < sizeof(touchbuttons)/sizeof(TouchButton); i++) {
272                 touchbuttons[i].x = (unsigned int)(touchbuttons[i].fx * w);
273                 touchbuttons[i].y = (unsigned int)(touchbuttons[i].fy * h);
274                 touchbuttons[i].x2 = (unsigned int)(touchbuttons[i].x + touchbuttons[i].fw * w);
275                 touchbuttons[i].y2 = (unsigned int)(touchbuttons[i].y + touchbuttons[i].fh * h);
276         }
277 }
278
279 template <typename T>
280 static void drawControls(T * buffer, const int pitch)
281 {
282         unsigned int i = 0;
283         int x, y;
284         const T black = static_cast<T>(0xFFFFFFFFU);
285         T* temp;
286
287         for (i = 0; i < sizeof(touchbuttons)/sizeof(TouchButton); i++) {
288                 temp = buffer + touchbuttons[i].y * pitch + touchbuttons[i].x;
289                 for (x = touchbuttons[i].x; x < touchbuttons[i].x2; x++) {
290                         *temp = black;
291                         temp++;
292                 }
293                 temp = buffer + touchbuttons[i].y2 * pitch + touchbuttons[i].x;
294                 for (x = touchbuttons[i].x; x < touchbuttons[i].x2; x++) {
295                         *temp = black;
296                         temp++;
297                 }
298                 temp = buffer + touchbuttons[i].y * pitch + touchbuttons[i].x;
299                 for (y = touchbuttons[i].y; y < touchbuttons[i].y2; y++) {
300                         *temp = black;
301                         temp+=pitch;
302                 }
303                 temp = buffer + touchbuttons[i].y * pitch + touchbuttons[i].x2;
304                 for (y = touchbuttons[i].y; y < touchbuttons[i].y2; y++) {
305                         *temp = black;
306                         temp+=pitch;
307                 }
308         }
309 }
310
311 void S9xInputScreenDraw(int pixelSize, void * buffer, int pitch)
312 {
313         switch (pixelSize)
314         {
315                 case 1:
316                         drawControls(reinterpret_cast<uint8*>(buffer), pitch);
317                         break;
318                 case 2:
319                         drawControls(reinterpret_cast<uint16*>(buffer), pitch / 2);
320                         break;
321         }
322 }
323