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