Port the generic control stuff from wxwidgets work
[lsnes.git] / generic / controller.cpp
blob7405a2a23f51366547641ca31484e7f28d0d4600
1 #include "controller.hpp"
2 #include "lsnes.hpp"
3 #include "mainloop.hpp"
4 #include <map>
5 #include <sstream>
6 #include "window.hpp"
7 #include "command.hpp"
8 #include "framebuffer.hpp"
9 #include <snes/snes.hpp>
10 #include <ui-libsnes/libsnes.hpp>
12 #define BUTTON_LEFT 0 //Gamepad
13 #define BUTTON_RIGHT 1 //Gamepad
14 #define BUTTON_UP 2 //Gamepad
15 #define BUTTON_DOWN 3 //Gamepad
16 #define BUTTON_A 4 //Gamepad
17 #define BUTTON_B 5 //Gamepad
18 #define BUTTON_X 6 //Gamepad
19 #define BUTTON_Y 7 //Gamepad
20 #define BUTTON_L 8 //Gamepad & Mouse
21 #define BUTTON_R 9 //Gamepad & Mouse
22 #define BUTTON_SELECT 10 //Gamepad
23 #define BUTTON_START 11 //Gamepad & Justifier
24 #define BUTTON_TRIGGER 12 //Superscope.
25 #define BUTTON_CURSOR 13 //Superscope & Justifier
26 #define BUTTON_PAUSE 14 //Superscope
27 #define BUTTON_TURBO 15 //Superscope
29 namespace
31 porttype_t porttypes[2];
32 int analog_indices[3] = {-1, -1, -1};
33 bool analog_is_mouse[3];
34 //Current controls.
35 controls_t curcontrols;
36 controls_t autoheld_controls;
37 std::vector<controls_t> autofire_pattern;
39 void update_analog_indices() throw()
41 int i = 0;
42 for(unsigned j = 0; j < sizeof(analog_indices) / sizeof(analog_indices[0]); j++)
43 analog_indices[j] = -1;
44 for(unsigned j = 0; j < 8; j++) {
45 devicetype_t d = controller_type_by_logical(j);
46 switch(d) {
47 case DT_NONE:
48 case DT_GAMEPAD:
49 break;
50 case DT_MOUSE:
51 analog_is_mouse[i] = true;
52 analog_indices[i++] = j;
53 break;
54 case DT_SUPERSCOPE:
55 case DT_JUSTIFIER:
56 analog_is_mouse[i] = false;
57 analog_indices[i++] = j;
58 break;
63 std::map<std::string, std::pair<unsigned, unsigned>> buttonmap;
65 const char* buttonnames[] = {
66 "left", "right", "up", "down", "A", "B", "X", "Y", "L", "R", "select", "start", "trigger", "cursor",
67 "pause", "turbo"
70 void init_buttonmap()
72 static int done = 0;
73 if(done)
74 return;
75 for(unsigned i = 0; i < 8; i++)
76 for(unsigned j = 0; j < sizeof(buttonnames) / sizeof(buttonnames[0]); j++) {
77 std::ostringstream x;
78 x << (i + 1) << buttonnames[j];
79 buttonmap[x.str()] = std::make_pair(i, j);
81 done = 1;
84 //Do button action.
85 void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor, controls_t& c)
87 enum devicetype_t p = controller_type_by_logical(ui_id);
88 int x = controller_index_by_logical(ui_id);
89 int bid = -1;
90 switch(p) {
91 case DT_NONE:
92 window::out() << "No such controller #" << (ui_id + 1) << std::endl;
93 return;
94 case DT_GAMEPAD:
95 switch(button) {
96 case BUTTON_UP: bid = SNES_DEVICE_ID_JOYPAD_UP; break;
97 case BUTTON_DOWN: bid = SNES_DEVICE_ID_JOYPAD_DOWN; break;
98 case BUTTON_LEFT: bid = SNES_DEVICE_ID_JOYPAD_LEFT; break;
99 case BUTTON_RIGHT: bid = SNES_DEVICE_ID_JOYPAD_RIGHT; break;
100 case BUTTON_A: bid = SNES_DEVICE_ID_JOYPAD_A; break;
101 case BUTTON_B: bid = SNES_DEVICE_ID_JOYPAD_B; break;
102 case BUTTON_X: bid = SNES_DEVICE_ID_JOYPAD_X; break;
103 case BUTTON_Y: bid = SNES_DEVICE_ID_JOYPAD_Y; break;
104 case BUTTON_L: bid = SNES_DEVICE_ID_JOYPAD_L; break;
105 case BUTTON_R: bid = SNES_DEVICE_ID_JOYPAD_R; break;
106 case BUTTON_SELECT: bid = SNES_DEVICE_ID_JOYPAD_SELECT; break;
107 case BUTTON_START: bid = SNES_DEVICE_ID_JOYPAD_START; break;
108 default:
109 window::out() << "Invalid button for gamepad" << std::endl;
110 return;
112 break;
113 case DT_MOUSE:
114 switch(button) {
115 case BUTTON_L: bid = SNES_DEVICE_ID_MOUSE_LEFT; break;
116 case BUTTON_R: bid = SNES_DEVICE_ID_MOUSE_RIGHT; break;
117 default:
118 window::out() << "Invalid button for mouse" << std::endl;
119 return;
121 break;
122 case DT_JUSTIFIER:
123 switch(button) {
124 case BUTTON_START: bid = SNES_DEVICE_ID_JUSTIFIER_START; break;
125 case BUTTON_TRIGGER: bid = SNES_DEVICE_ID_JUSTIFIER_TRIGGER; break;
126 default:
127 window::out() << "Invalid button for justifier" << std::endl;
128 return;
130 break;
131 case DT_SUPERSCOPE:
132 switch(button) {
133 case BUTTON_TRIGGER: bid = SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER; break;
134 case BUTTON_CURSOR: bid = SNES_DEVICE_ID_SUPER_SCOPE_CURSOR; break;
135 case BUTTON_PAUSE: bid = SNES_DEVICE_ID_SUPER_SCOPE_PAUSE; break;
136 case BUTTON_TURBO: bid = SNES_DEVICE_ID_SUPER_SCOPE_TURBO; break;
137 default:
138 window::out() << "Invalid button for superscope" << std::endl;
139 return;
141 break;
143 if(do_xor)
144 c((x & 4) ? 1 : 0, x & 3, bid) ^= newstate;
145 else
146 c((x & 4) ? 1 : 0, x & 3, bid) = newstate;
149 //Do button action.
150 void do_button_action(unsigned ui_id, unsigned button, short newstate, bool do_xor = false)
152 if(do_xor) {
153 do_button_action(ui_id, button, newstate, do_xor, autoheld_controls);
154 int x = controller_index_by_logical(ui_id);
155 enum devicetype_t p = controller_type_by_logical(ui_id);
156 int y = get_physcial_id_for_control(p, button);
157 if(x >= 0 && y >= 0)
158 window_callback::do_autohold_update(x, y, get_autohold(x, y));
159 } else
160 do_button_action(ui_id, button, newstate, do_xor, curcontrols);
163 function_ptr_command<tokensplitter&> autofire("autofire", "Set autofire pattern",
164 "Syntax: autofire <buttons|->...\nSet autofire pattern\n",
165 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
166 if(!t)
167 throw std::runtime_error("Need at least one frame for autofire");
168 std::vector<controls_t> new_autofire_pattern;
169 init_buttonmap();
170 while(t) {
171 std::string fpattern = t;
172 if(fpattern == "-")
173 new_autofire_pattern.push_back(controls_t());
174 else {
175 controls_t c;
176 while(fpattern != "") {
177 size_t split = fpattern.find_first_of(",");
178 std::string button = fpattern;
179 std::string rest;
180 if(split < fpattern.length()) {
181 button = fpattern.substr(0, split);
182 rest = fpattern.substr(split + 1);
184 if(!buttonmap.count(button)) {
185 std::ostringstream x;
186 x << "Invalid button '" << button << "'";
187 throw std::runtime_error(x.str());
189 auto g = buttonmap[button];
190 do_button_action(g.first, g.second, 1, false, c);
191 fpattern = rest;
193 new_autofire_pattern.push_back(c);
196 autofire_pattern = new_autofire_pattern;
199 class button_action : public command
201 public:
202 button_action(const std::string& cmd, int _type, unsigned _controller, std::string _button)
203 throw(std::bad_alloc)
204 : command(cmd)
206 commandn = cmd;
207 type = _type;
208 controller = _controller;
209 button = _button;
211 ~button_action() throw() {}
212 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
214 if(args != "")
215 throw std::runtime_error("This command does not take parameters");
216 init_buttonmap();
217 if(!buttonmap.count(button))
218 return;
219 auto i = buttonmap[button];
220 do_button_action(i.first, i.second, (type != 1) ? 1 : 0, (type == 2));
221 update_movie_state();
222 window::notify_screen_update();
224 std::string get_short_help() throw(std::bad_alloc)
226 return "Press/Unpress button";
228 std::string get_long_help() throw(std::bad_alloc)
230 return "Syntax: " + commandn + "\n"
231 "Presses/Unpresses button\n";
233 std::string commandn;
234 unsigned controller;
235 int type;
236 std::string button;
239 class button_action_helper
241 public:
242 button_action_helper()
244 for(size_t i = 0; i < sizeof(buttonnames) / sizeof(buttonnames[0]); ++i)
245 for(int j = 0; j < 3; ++j)
246 for(unsigned k = 0; k < 8; ++k) {
247 std::ostringstream x, y;
248 switch(j) {
249 case 0:
250 x << "+controller";
251 break;
252 case 1:
253 x << "-controller";
254 break;
255 case 2:
256 x << "controllerh";
257 break;
259 x << (k + 1);
260 x << buttonnames[i];
261 y << (k + 1);
262 y << buttonnames[i];
263 new button_action(x.str(), j, k, y.str());
266 } bah;
270 int controller_index_by_logical(unsigned lid) throw()
272 bool p1multitap = (porttypes[0] == PT_MULTITAP);
273 unsigned p1devs = port_types[porttypes[0]].devices;
274 unsigned p2devs = port_types[porttypes[1]].devices;
275 if(lid >= p1devs + p2devs)
276 return -1;
277 if(!p1multitap)
278 if(lid < p1devs)
279 return lid;
280 else
281 return 4 + lid - p1devs;
282 else
283 if(lid == 0)
284 return 0;
285 else if(lid < 5)
286 return lid + 3;
287 else
288 return lid - 4;
291 int controller_index_by_analog(unsigned aid) throw()
293 if(aid > 2)
294 return -1;
295 return analog_indices[aid];
298 bool controller_ismouse_by_analog(unsigned aid) throw()
300 if(aid > 2)
301 return false;
302 return analog_is_mouse[aid];
305 devicetype_t controller_type_by_logical(unsigned lid) throw()
307 int x = controller_index_by_logical(lid);
308 if(x < 0)
309 return DT_NONE;
310 enum porttype_t rawtype = porttypes[x >> 2];
311 if((x & 3) < port_types[rawtype].devices)
312 return port_types[rawtype].dtype;
313 else
314 return DT_NONE;
317 void controller_set_port_type(unsigned port, porttype_t ptype, bool set_core) throw()
319 if(set_core && ptype != PT_INVALID)
320 snes_set_controller_port_device(port != 0, port_types[ptype].bsnes_type);
321 porttypes[port] = ptype;
322 update_analog_indices();
323 window_callback::do_autohold_reconfigure();
326 controls_t get_current_controls(uint64_t frame)
328 if(autofire_pattern.size())
329 return curcontrols ^ autoheld_controls ^ autofire_pattern[frame % autofire_pattern.size()];
330 else
331 return curcontrols ^ autoheld_controls;
334 void send_analog_input(int32_t x, int32_t y, unsigned index)
336 if(controller_ismouse_by_analog(index)) {
337 x -= (framebuffer.width / 2);
338 y -= (framebuffer.height / 2);
339 } else {
340 auto g = get_scale_factors(framebuffer.width, framebuffer.height);
341 x /= g.first;
342 y /= g.second;
344 int aindex = controller_index_by_analog(index);
345 if(aindex < 0) {
346 window::out() << "No analog controller in slot #" << (index + 1) << std::endl;
347 return;
349 curcontrols(aindex >> 2, aindex & 3, 0) = x;
350 curcontrols(aindex >> 2, aindex & 3, 1) = y;
353 void set_curcontrols_reset(int32_t delay)
355 if(delay >= 0) {
356 curcontrols(CONTROL_SYSTEM_RESET) = 1;
357 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
358 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
359 } else {
360 curcontrols(CONTROL_SYSTEM_RESET) = 0;
361 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = 0;
362 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = 0;
367 void change_autohold(unsigned pid, unsigned idx, bool newstate)
369 if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
370 return;
371 autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) = (newstate ? 1 : 0);
372 window_callback::do_autohold_update(pid, idx, newstate);
373 update_movie_state();
374 window::notify_screen_update();
377 bool get_autohold(unsigned pid, unsigned idx)
379 if(pid >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || idx >= CONTROLLER_CONTROLS)
380 return false;
381 return (autoheld_controls(pid / MAX_CONTROLLERS_PER_PORT, pid % MAX_CONTROLLERS_PER_PORT, idx) != 0);
384 std::string get_button_name(unsigned lidx)
386 if(lidx < 16)
387 return buttonnames[lidx];
388 else
389 return "";
392 int get_physcial_id_for_control(devicetype_t dtype, unsigned lidx)
394 switch(dtype) {
395 case DT_NONE: return -1;
396 case DT_GAMEPAD:
397 switch(lidx) {
398 case BUTTON_UP: return SNES_DEVICE_ID_JOYPAD_UP;
399 case BUTTON_DOWN: return SNES_DEVICE_ID_JOYPAD_DOWN;
400 case BUTTON_LEFT: return SNES_DEVICE_ID_JOYPAD_LEFT;
401 case BUTTON_RIGHT: return SNES_DEVICE_ID_JOYPAD_RIGHT;
402 case BUTTON_A: return SNES_DEVICE_ID_JOYPAD_A;
403 case BUTTON_B: return SNES_DEVICE_ID_JOYPAD_B;
404 case BUTTON_X: return SNES_DEVICE_ID_JOYPAD_X;
405 case BUTTON_Y: return SNES_DEVICE_ID_JOYPAD_Y;
406 case BUTTON_L: return SNES_DEVICE_ID_JOYPAD_L;
407 case BUTTON_R: return SNES_DEVICE_ID_JOYPAD_R;
408 case BUTTON_SELECT: return SNES_DEVICE_ID_JOYPAD_SELECT;
409 case BUTTON_START: return SNES_DEVICE_ID_JOYPAD_START;
410 default: return -1;
412 case DT_MOUSE:
413 switch(lidx) {
414 case BUTTON_L: return SNES_DEVICE_ID_MOUSE_LEFT;
415 case BUTTON_R: return SNES_DEVICE_ID_MOUSE_RIGHT;
416 default: return -1;
418 case DT_SUPERSCOPE:
419 switch(lidx) {
420 case BUTTON_TRIGGER: return SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER;
421 case BUTTON_CURSOR: return SNES_DEVICE_ID_SUPER_SCOPE_CURSOR;
422 case BUTTON_PAUSE: return SNES_DEVICE_ID_SUPER_SCOPE_PAUSE;
423 case BUTTON_TURBO: return SNES_DEVICE_ID_SUPER_SCOPE_TURBO;
424 default: return -1;
426 case DT_JUSTIFIER:
427 switch(lidx) {
428 case BUTTON_START: return SNES_DEVICE_ID_JUSTIFIER_START;
429 case BUTTON_TRIGGER: return SNES_DEVICE_ID_JUSTIFIER_TRIGGER;
430 default: return -1;
432 default: return -1;