Allow immediate saving at point of save
[lsnes.git] / src / core / controller.cpp
blobfe1897209943954ec8ae2b0cac349cf2ef2e8746
1 #include "lsnes.hpp"
2 #include "core/emucore.hpp"
4 #include "core/command.hpp"
5 #include "core/controller.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framebuffer.hpp"
8 #include "core/mainloop.hpp"
9 #include "core/misc.hpp"
10 #include "core/window.hpp"
11 #include "library/string.hpp"
13 #include <map>
14 #include <sstream>
16 namespace
18 std::map<std::string, std::pair<unsigned, unsigned>> buttonmap;
20 void init_buttonmap()
22 static int done = 0;
23 if(done)
24 return;
25 auto lim = get_core_logical_controller_limits();
26 for(unsigned i = 0; i < lim.first; i++)
27 for(unsigned j = 0; j < lim.second; j++) {
28 buttonmap[(stringfmt() << (i + 1) << get_logical_button_name(j)).str()] =
29 std::make_pair(i, j);
31 done = 1;
34 //Do button action.
35 void do_button_action(unsigned ui_id, unsigned button, short newstate, int mode)
37 int x = controls.lcid_to_pcid(ui_id);
38 if(x < 0) {
39 messages << "No such controller #" << (ui_id + 1) << std::endl;
40 return;
42 int bid = controls.button_id(x, button);
43 if(bid < 0) {
44 messages << "Invalid button for controller type" << std::endl;
45 return;
47 if(mode == 1) {
48 //Autohold.
49 int16_t nstate = controls.autohold(x, bid) ^ newstate;
50 if(lua_callback_do_button(x / 4 + 1, x % 4, bid, nstate ? "hold" : "unhold"))
51 return;
52 controls.autohold(x, bid, nstate);
53 information_dispatch::do_autohold_update(x, bid, controls.autohold(x, bid));
54 } else if(mode == 2) {
55 //Framehold.
56 bool nstate = controls.framehold(x, bid) ^ newstate;
57 if(lua_callback_do_button(x / 4 + 1, x % 4, bid, nstate ? "type" : "untype"))
58 return;
59 controls.framehold(x, bid, nstate);
60 if(nstate)
61 messages << "Holding " << (ui_id + 1) << get_logical_button_name(button)
62 << " for the next frame" << std::endl;
63 else
64 messages << "Not holding " << (ui_id + 1) << get_logical_button_name(button)
65 << " for the next frame" << std::endl;
66 } else {
67 if(lua_callback_do_button(x / 4 + 1, x % 4, bid, newstate ? "press" : "release"))
68 return;
69 controls.button(x, bid, newstate);
73 void send_analog(unsigned lcid, int32_t x, int32_t y)
75 int pcid = controls.lcid_to_pcid(lcid);
76 if(pcid < 0) {
77 messages << "Controller #" << (lcid + 1) << " not present" << std::endl;
78 return;
80 if(controls.is_analog(pcid) < 0) {
81 messages << "Controller #" << (lcid + 1) << " is not analog" << std::endl;
82 return;
84 if(lua_callback_do_button(pcid / 4 + 1, x % 4, 0, "analog"))
85 return;
86 if(lua_callback_do_button(pcid / 4 + 1, x % 4, 1, "analog"))
87 return;
88 auto g2 = get_framebuffer_size();
89 if(controls.is_mouse(pcid)) {
90 controls.analog(pcid, x - g2.first / 2, y - g2.second / 2);
91 } else
92 controls.analog(pcid, x / 2 , y / 2);
95 function_ptr_command<const std::string&> autofire("autofire", "Set autofire pattern",
96 "Syntax: autofire <buttons|->...\nSet autofire pattern\n",
97 [](const std::string& a) throw(std::bad_alloc, std::runtime_error) {
98 auto r = regex(".*[^ \t].*", a, "Need at least one frame for autofire");
99 std::vector<controller_frame> new_autofire_pattern;
100 init_buttonmap();
101 std::string pattern = a;
102 while(pattern != "") {
103 std::string fpattern;
104 extract_token(pattern, fpattern, " \t", true);
105 if(fpattern == "-")
106 new_autofire_pattern.push_back(controls.get_blank());
107 else {
108 controller_frame c(controls.get_blank());
109 while(fpattern != "") {
110 size_t split = fpattern.find_first_of(",");
111 std::string button = fpattern;
112 std::string rest;
113 if(split < fpattern.length()) {
114 button = fpattern.substr(0, split);
115 rest = fpattern.substr(split + 1);
117 if(!buttonmap.count(button))
118 (stringfmt() << "Invalid button '" << button << "'").throwex();
119 auto g = buttonmap[button];
120 int x = controls.lcid_to_pcid(g.first);
121 if(x < 0)
122 (stringfmt() << "No such controller #" << (g.first + 1)).
123 throwex();
124 int bid = controls.button_id(x, g.second);
125 if(bid < 0)
126 (stringfmt() << "Invalid button for controller type").
127 throwex();
128 c.axis(x, bid, true);
129 fpattern = rest;
131 new_autofire_pattern.push_back(c);
134 controls.autofire(new_autofire_pattern);
137 class button_action : public command
139 public:
140 button_action(const std::string& cmd, int _type, unsigned _controller, std::string _button)
141 throw(std::bad_alloc)
142 : command(cmd)
144 commandn = cmd;
145 type = _type;
146 controller = _controller;
147 button = _button;
149 ~button_action() throw() {}
150 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
152 if(args != "")
153 throw std::runtime_error("This command does not take parameters");
154 init_buttonmap();
155 if(!buttonmap.count(button))
156 return;
157 auto i = buttonmap[button];
158 if(type == 0)
159 do_button_action(i.first, i.second, 1, 0);
160 else if(type == 1)
161 do_button_action(i.first, i.second, 0, 0);
162 else if(type == 2)
163 do_button_action(i.first, i.second, 1, 1);
164 else if(type == 3)
165 do_button_action(i.first, i.second, 1, 2);
166 update_movie_state();
167 information_dispatch::do_status_update();
169 std::string get_short_help() throw(std::bad_alloc)
171 return "Press/Unpress button";
173 std::string get_long_help() throw(std::bad_alloc)
175 return "Syntax: " + commandn + "\n"
176 "Presses/Unpresses button\n";
178 std::string commandn;
179 unsigned controller;
180 int type;
181 std::string button;
184 class analog_action : public command
186 public:
187 analog_action(const std::string& cmd, unsigned _controller)
188 throw(std::bad_alloc)
189 : command(cmd)
191 controller = _controller;
193 ~analog_action() throw() {}
194 void invoke(const std::string& args) throw(std::bad_alloc, std::runtime_error)
196 if(args != "")
197 throw std::runtime_error("This command does not take parameters");
198 keygroup* mouse_x = keygroup::lookup_by_name("mouse_x");
199 keygroup* mouse_y = keygroup::lookup_by_name("mouse_y");
200 if(!mouse_x || !mouse_y) {
201 messages << "Controller analog function not available without mouse" << std::endl;
202 return;
204 send_analog(controller, mouse_x->get_value(), mouse_y->get_value());
206 private:
207 unsigned controller;
210 class button_action_helper
212 public:
213 button_action_helper()
215 auto lim = get_core_logical_controller_limits();
216 for(size_t i = 0; i < lim.second; ++i)
217 for(int j = 0; j < 4; ++j)
218 for(unsigned k = 0; k < lim.first; ++k) {
219 stringfmt x, y, expx;
220 switch(j) {
221 case 0:
222 x << "+controller";
223 break;
224 case 1:
225 x << "-controller";
226 break;
227 case 2:
228 x << "controllerh";
229 break;
230 case 3:
231 x << "controllerf";
232 break;
234 x << (k + 1) << get_logical_button_name(i);
235 y << (k + 1) << get_logical_button_name(i);
236 expx << "Controller‣" << (k + 1) << "‣" << get_logical_button_name(i);
237 our_commands.insert(new button_action(x.str(), j, k, y.str()));
238 if(j == 0)
239 our_icommands.insert(new inverse_key(x.str(), expx.str()));
240 if(j == 2)
241 our_icommands.insert(new inverse_key(x.str(), expx.str() +
242 " (hold)"));
243 if(j == 3)
244 our_icommands.insert(new inverse_key(x.str(), expx.str() +
245 " (typed)"));
247 if(get_core_need_analog())
248 for(unsigned k = 0; k < lim.first; ++k) {
249 stringfmt x, expx;
250 x << "controller" << (k + 1) << "analog";
251 expx << "Controller‣" << (k + 1) << "‣Analog function";
252 our_commands.insert(new analog_action(x.str(), k));
253 our_icommands.insert(new inverse_key(x.str(), expx.str()));
256 ~button_action_helper()
258 for(auto i : our_commands)
259 delete i;
260 for(auto i : our_icommands)
261 delete i;
262 our_commands.clear();
263 our_icommands.clear();
265 std::set<command*> our_commands;
266 std::set<inverse_key*> our_icommands;
267 } bah;
270 controller_state controls;