Autofire: Fix false status indications
[lsnes.git] / src / platform / wxwidgets / editor-autohold.cpp
blob527d3a7fe464a570b3fff96d182ccfb1de7a3f26
1 #include "core/controller.hpp"
2 #include "core/movie.hpp"
3 #include "core/moviedata.hpp"
4 #include "core/dispatch.hpp"
5 #include "core/window.hpp"
7 #include "interface/controller.hpp"
8 #include "core/mainloop.hpp"
9 #include "platform/wxwidgets/platform.hpp"
10 #include "platform/wxwidgets/textrender.hpp"
11 #include "library/minmax.hpp"
12 #include "library/string.hpp"
13 #include "library/utf8.hpp"
15 #include <algorithm>
16 #include <cstring>
17 #include <wx/wx.h>
18 #include <wx/event.h>
19 #include <wx/control.h>
20 #include <wx/combobox.h>
21 #include <wx/statline.h>
23 class wxeditor_autohold : public wxDialog, public information_dispatch
25 public:
26 wxeditor_autohold(wxWindow* parent);
27 ~wxeditor_autohold() throw();
28 bool ShouldPreventAppExit() const;
29 void on_wclose(wxCloseEvent& e);
30 void on_checkbox(wxCommandEvent& e);
31 void on_autohold_update(unsigned port, unsigned controller, unsigned ctrlnum, bool newstate);
32 void on_autofire_update(unsigned port, unsigned controller, unsigned ctrlnum, unsigned duty,
33 unsigned cyclelen);
34 void on_autohold_reconfigure();
35 private:
36 struct control_triple
38 unsigned port;
39 unsigned controller;
40 unsigned index;
41 int afid;
42 wxStaticText* label; //Used only by UI version.
43 wxCheckBox* check; //Used only by UI version.
44 wxCheckBox* afcheck; //Used only by UI version.
45 bool status; //Used only by internal version.
46 bool afstatus; //Used only by internal version.
47 unsigned logical; //Logical controller. Internal only.
48 std::string name; //Name. Internal only.
50 struct controller_double
52 wxPanel* panel;
53 wxStaticBox* box;
54 wxStaticText* label;
55 wxSizer* rtop;
56 wxSizer* top;
57 wxSizer* grid;
59 std::map<int, control_triple> autoholds;
60 std::vector<controller_double> panels;
61 void update_controls();
62 bool closing;
63 wxBoxSizer* hsizer;
66 namespace
68 wxeditor_autohold* autohold_open;
71 wxeditor_autohold::~wxeditor_autohold() throw() {}
73 wxeditor_autohold::wxeditor_autohold(wxWindow* parent)
74 : wxDialog(parent, wxID_ANY, wxT("lsnes: Autohold/Autofire"), wxDefaultPosition, wxSize(-1, -1)),
75 information_dispatch("autohold-listener")
77 closing = false;
78 Centre();
79 hsizer = new wxBoxSizer(wxHORIZONTAL);
80 SetSizer(hsizer);
81 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_autohold::on_wclose));
82 update_controls();
83 hsizer->SetSizeHints(this);
84 Fit();
87 void wxeditor_autohold::on_autohold_update(unsigned port, unsigned controller, unsigned ctrlnum, bool newstate)
89 runuifun([this, port, controller, ctrlnum, newstate]() {
90 for(auto i : this->autoholds) {
91 if(i.second.port != port) continue;
92 if(i.second.controller != controller) continue;
93 if(i.second.index != ctrlnum) continue;
94 i.second.check->SetValue(newstate);
96 });
99 void wxeditor_autohold::on_autofire_update(unsigned port, unsigned controller, unsigned ctrlnum, unsigned duty,
100 unsigned cyclelen)
102 runuifun([this, port, controller, ctrlnum, duty]() {
103 for(auto i : this->autoholds) {
104 if(i.second.port != port) continue;
105 if(i.second.controller != controller) continue;
106 if(i.second.index != ctrlnum) continue;
107 i.second.afcheck->SetValue(duty != 0);
112 void wxeditor_autohold::on_autohold_reconfigure()
114 runuifun([this]() {
115 try {
116 this->update_controls();
117 } catch(std::runtime_error& e) {
118 //Close the window.
119 bool wasc = closing;
120 closing = true;
121 autohold_open = NULL;
122 if(!wasc)
123 Destroy();
128 void wxeditor_autohold::on_checkbox(wxCommandEvent& e)
130 int id = e.GetId();
131 if(!autoholds.count(id))
132 return;
133 auto t = autoholds[id];
134 bool isaf = (t.afid == id);
135 bool newstate = isaf ? t.afcheck->IsChecked() : t.check->IsChecked();
136 bool state = false;
137 runemufn([t, newstate, &state, isaf]() {
138 if(isaf) {
139 auto _state = controls.autofire2(t.port, t.controller, t.index);
140 state = (_state.first != 0);
141 if(lua_callback_do_button(t.port, t.controller, t.index, newstate ? "autofire 1 2" :
142 "autofire"))
143 return;
144 controls.autofire2(t.port, t.controller, t.index, newstate ? 1 : 0, newstate ? 2 : 1);
145 state = newstate;
146 } else {
147 state = controls.autohold2(t.port, t.controller, t.index);
148 if(lua_callback_do_button(t.port, t.controller, t.index, newstate ? "hold" : "unhold"))
149 return;
150 controls.autohold2(t.port, t.controller, t.index, newstate);
151 state = newstate;
154 if(isaf)
155 t.afcheck->SetValue(state);
156 else
157 t.check->SetValue(state);
160 void wxeditor_autohold::update_controls()
162 for(auto i : autoholds) {
163 if(i.first != i.second.afid)
164 i.second.label->Destroy();
165 if(i.first != i.second.afid)
166 i.second.check->Destroy();
167 if(i.first == i.second.afid)
168 i.second.afcheck->Destroy();
170 for(auto i : panels) {
171 hsizer->Detach(i.panel);
172 i.panel->Destroy();
174 autoholds.clear();
175 panels.clear();
176 std::vector<control_triple> _autoholds;
177 std::vector<std::string> _controller_labels;
178 runemufn([&_autoholds, &_controller_labels](){
179 std::map<std::string, unsigned> next_in_class;
180 controller_frame model = controls.get_blank();
181 const port_type_set& pts = model.porttypes();
182 unsigned pcnt = pts.ports();
183 unsigned cnum_g = 0;
184 for(unsigned i = 0;; i++) {
185 auto pcid = controls.lcid_to_pcid(i);
186 if(pcid.first < 0)
187 break;
188 const port_type& pt = pts.port_type(pcid.first);
189 const port_controller_set& pci = *(pt.controller_info);
190 if(pci.controller_count <= pcid.second || !pci.controllers[pcid.second])
191 continue;
192 const port_controller& pc = *(pci.controllers[pcid.second]);
193 //First check that this has non-hidden buttons.
194 bool has_buttons = false;
195 for(unsigned k = 0; k < pc.button_count; k++) {
196 if(!pc.buttons[k])
197 continue;
198 const port_controller_button& pcb = *(pc.buttons[k]);
199 if(pcb.type == port_controller_button::TYPE_BUTTON && !pcb.shadow)
200 has_buttons = true;
202 if(!has_buttons)
203 continue;
204 //Okay, a valid controller.
205 if(!next_in_class.count(pc.cclass))
206 next_in_class[pc.cclass] = 1;
207 uint32_t cnum = next_in_class[pc.cclass]++;
208 _controller_labels.push_back((stringfmt() << pc.cclass << "-" << cnum).str());
209 for(unsigned k = 0; k < pc.button_count; k++) {
210 if(!pc.buttons[k])
211 continue;
212 const port_controller_button& pcb = *(pc.buttons[k]);
213 if(pcb.type != port_controller_button::TYPE_BUTTON || pcb.shadow)
214 continue;
215 struct control_triple t;
216 t.port = pcid.first;
217 t.controller = pcid.second;
218 t.index = k;
219 t.status = controls.autohold2(pcid.first, pcid.second, k);
220 auto h = controls.autofire2(pcid.first, pcid.second, k);
221 t.afstatus = (h.first > 0);
222 t.logical = cnum_g;
223 t.name = pcb.name;
224 _autoholds.push_back(t);
226 cnum_g++;
229 int next_id = wxID_HIGHEST + 1;
230 unsigned last_logical = 0xFFFFFFFFUL;
231 wxSizer* current;
232 wxPanel* current_p;
233 wxSizer* current_t = NULL;
234 for(auto i : _autoholds) {
235 if(i.logical != last_logical) {
236 //New controller starts.
237 if(current_t) {
238 hsizer->Add(current_p);
239 current_t->SetSizeHints(current_p);
240 current_t->Fit(current_p);
242 controller_double d;
243 current_p = d.panel = new wxPanel(this, wxID_ANY);
244 current_t = d.rtop = new wxBoxSizer(wxVERTICAL);
245 d.panel->SetSizer(d.rtop);
246 d.box = new wxStaticBox(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
247 d.top = new wxStaticBoxSizer(d.box, wxVERTICAL);
248 #ifdef __WXMAC__
249 d.label = new wxStaticText(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
250 d.top->Add(d.label);
251 #endif
252 current = d.grid = new wxFlexGridSizer(0, 3, 0, 0);
253 d.top->Add(d.grid);
254 d.rtop->Add(d.top);
255 panels.push_back(d);
256 last_logical = i.logical;
258 wxStaticText* label = new wxStaticText(current_p, wxID_ANY, towxstring(i.name));
259 wxCheckBox* check = new wxCheckBox(current_p, next_id, wxT("Hold"));
260 wxCheckBox* afcheck = new wxCheckBox(current_p, next_id + 1, wxT("Rapid"));
261 struct control_triple t;
262 t.port = i.port;
263 t.controller = i.controller;
264 t.index = i.index;
265 t.label = label;
266 t.check = check;
267 t.afcheck = afcheck;
268 t.afid = next_id + 1;
269 check->SetValue(i.status);
270 check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(wxeditor_autohold::on_checkbox),
271 NULL, this);
272 afcheck->SetValue(i.afstatus);
273 afcheck->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
274 wxCommandEventHandler(wxeditor_autohold::on_checkbox), NULL, this);
275 current->Add(label);
276 current->Add(check);
277 current->Add(afcheck);
278 autoholds[next_id++] = t;
279 autoholds[next_id++] = t;
281 if(current_t) {
282 hsizer->Add(current_p);
283 current_t->SetSizeHints(current_p);
284 current_t->Fit(current_p);
286 if(_autoholds.empty()) {
287 throw std::runtime_error("No controlers");
289 hsizer->SetSizeHints(this);
290 hsizer->Layout();
291 hsizer->Fit(this);
292 Fit();
295 bool wxeditor_autohold::ShouldPreventAppExit() const { return false; }
297 void wxeditor_autohold::on_wclose(wxCloseEvent& e)
299 bool wasc = closing;
300 closing = true;
301 autohold_open = NULL;
302 if(!wasc)
303 Destroy();
306 void wxeditor_autohold_display(wxWindow* parent)
308 if(autohold_open)
309 return;
310 wxeditor_autohold* v;
311 try {
312 v = new wxeditor_autohold(parent);
313 } catch(std::runtime_error& e) {
314 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
315 return;
317 v->Show();
318 autohold_open = v;