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