Evdev joystick plugin
[lsnes.git] / generic / controller.cpp
blob586b75ec3e1133bf3a7d0739b9d602af5fb2ab77
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 else
155 do_button_action(ui_id, button, newstate, do_xor, curcontrols);
158 function_ptr_command<tokensplitter&> autofire("autofire", "Set autofire pattern",
159 "Syntax: autofire <buttons|->...\nSet autofire pattern\n",
160 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
161 if(!t)
162 throw std::runtime_error("Need at least one frame for autofire");
163 std::vector<controls_t> new_autofire_pattern;
164 init_buttonmap();
165 while(t) {
166 std::string fpattern = t;
167 if(fpattern == "-")
168 new_autofire_pattern.push_back(controls_t());
169 else {
170 controls_t c;
171 while(fpattern != "") {
172 size_t split = fpattern.find_first_of(",");
173 std::string button = fpattern;
174 std::string rest;
175 if(split < fpattern.length()) {
176 button = fpattern.substr(0, split);
177 rest = fpattern.substr(split + 1);
179 if(!buttonmap.count(button)) {
180 std::ostringstream x;
181 x << "Invalid button '" << button << "'";
182 throw std::runtime_error(x.str());
184 auto g = buttonmap[button];
185 do_button_action(g.first, g.second, 1, false, c);
186 fpattern = rest;
188 new_autofire_pattern.push_back(c);
191 autofire_pattern = new_autofire_pattern;
194 class button_action : public command
196 public:
197 button_action(const std::string& cmd, int _type, unsigned _controller, std::string _button)
198 throw(std::bad_alloc)
199 : command(cmd)
201 commandn = cmd;
202 type = _type;
203 controller = _controller;
204 button = _button;
206 ~button_action() throw() {}
207 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
209 if(args != "")
210 throw std::runtime_error("This command does not take parameters");
211 init_buttonmap();
212 if(!buttonmap.count(button))
213 return;
214 auto i = buttonmap[button];
215 do_button_action(i.first, i.second, (type != 1) ? 1 : 0, (type == 2));
216 update_movie_state();
217 window::notify_screen_update();
219 std::string get_short_help() throw(std::bad_alloc)
221 return "Press/Unpress button";
223 std::string get_long_help() throw(std::bad_alloc)
225 return "Syntax: " + commandn + "\n"
226 "Presses/Unpresses button\n";
228 std::string commandn;
229 unsigned controller;
230 int type;
231 std::string button;
234 class button_action_helper
236 public:
237 button_action_helper()
239 for(size_t i = 0; i < sizeof(buttonnames) / sizeof(buttonnames[0]); ++i)
240 for(int j = 0; j < 3; ++j)
241 for(unsigned k = 0; k < 8; ++k) {
242 std::ostringstream x, y;
243 switch(j) {
244 case 0:
245 x << "+controller";
246 break;
247 case 1:
248 x << "-controller";
249 break;
250 case 2:
251 x << "controllerh";
252 break;
254 x << (k + 1);
255 x << buttonnames[i];
256 y << (k + 1);
257 y << buttonnames[i];
258 new button_action(x.str(), j, k, y.str());
261 } bah;
265 int controller_index_by_logical(unsigned lid) throw()
267 bool p1multitap = (porttypes[0] == PT_MULTITAP);
268 unsigned p1devs = port_types[porttypes[0]].devices;
269 unsigned p2devs = port_types[porttypes[1]].devices;
270 if(lid >= p1devs + p2devs)
271 return -1;
272 if(!p1multitap)
273 if(lid < p1devs)
274 return lid;
275 else
276 return 4 + lid - p1devs;
277 else
278 if(lid == 0)
279 return 0;
280 else if(lid < 5)
281 return lid + 3;
282 else
283 return lid - 4;
286 int controller_index_by_analog(unsigned aid) throw()
288 if(aid > 2)
289 return -1;
290 return analog_indices[aid];
293 bool controller_ismouse_by_analog(unsigned aid) throw()
295 if(aid > 2)
296 return false;
297 return analog_is_mouse[aid];
300 devicetype_t controller_type_by_logical(unsigned lid) throw()
302 int x = controller_index_by_logical(lid);
303 if(x < 0)
304 return DT_NONE;
305 enum porttype_t rawtype = porttypes[x >> 2];
306 if((x & 3) < port_types[rawtype].devices)
307 return port_types[rawtype].dtype;
308 else
309 return DT_NONE;
312 void controller_set_port_type(unsigned port, porttype_t ptype, bool set_core) throw()
314 if(set_core && ptype != PT_INVALID)
315 snes_set_controller_port_device(port != 0, port_types[ptype].bsnes_type);
316 porttypes[port] = ptype;
317 update_analog_indices();
320 controls_t get_current_controls(uint64_t frame)
322 if(autofire_pattern.size())
323 return curcontrols ^ autoheld_controls ^ autofire_pattern[frame % autofire_pattern.size()];
324 else
325 return curcontrols ^ autoheld_controls;
328 void send_analog_input(int32_t x, int32_t y, unsigned index)
330 if(controller_ismouse_by_analog(index)) {
331 x -= (framebuffer.width / 2);
332 y -= (framebuffer.height / 2);
333 } else {
334 auto g = get_scale_factors(framebuffer.width, framebuffer.height);
335 x /= g.first;
336 y /= g.second;
338 int aindex = controller_index_by_analog(index);
339 if(aindex < 0) {
340 window::out() << "No analog controller in slot #" << (index + 1) << std::endl;
341 return;
343 curcontrols(aindex >> 2, aindex & 3, 0) = x;
344 curcontrols(aindex >> 2, aindex & 3, 1) = y;
347 void set_curcontrols_reset(int32_t delay)
349 if(delay >= 0) {
350 curcontrols(CONTROL_SYSTEM_RESET) = 1;
351 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = delay / 10000;
352 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = delay % 10000;
353 } else {
354 curcontrols(CONTROL_SYSTEM_RESET) = 0;
355 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI) = 0;
356 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO) = 0;