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