Fix scaling-related crashes
[lsnes.git] / src / platform / wxwidgets / editor-autohold.cpp
bloba5c5ba9e3b9419b310ee9e3d6d75729efd5999d4
1 #include <wx/wx.h>
2 #include <wx/event.h>
3 #include <wx/control.h>
4 #include <wx/combobox.h>
5 #include <wx/statline.h>
7 #include "core/controller.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/instance.hpp"
10 #include "core/mainloop.hpp"
11 #include "core/movie.hpp"
12 #include "core/moviedata.hpp"
13 #include "core/window.hpp"
15 #include "interface/controller.hpp"
16 #include "platform/wxwidgets/platform.hpp"
17 #include "platform/wxwidgets/textrender.hpp"
18 #include "library/minmax.hpp"
19 #include "library/string.hpp"
20 #include "library/utf8.hpp"
21 #include "lua/lua.hpp"
23 #include <algorithm>
24 #include <cstring>
26 class wxeditor_autohold : public wxDialog
28 public:
29 wxeditor_autohold(wxWindow* parent, emulator_instance& _inst);
30 ~wxeditor_autohold() throw();
31 bool ShouldPreventAppExit() const;
32 void on_wclose(wxCloseEvent& e);
33 void on_checkbox(wxCommandEvent& e);
34 private:
35 struct dispatch::target<unsigned, unsigned, unsigned, bool> ahupdate;
36 struct dispatch::target<unsigned, unsigned, unsigned, unsigned, unsigned> afupdate;
37 struct dispatch::target<> ahreconfigure;
39 struct control_triple
41 unsigned port;
42 unsigned controller;
43 unsigned index;
44 int afid;
45 wxStaticText* label; //Used only by UI version.
46 wxCheckBox* check; //Used only by UI version.
47 wxCheckBox* afcheck; //Used only by UI version.
48 bool status; //Used only by internal version.
49 bool afstatus; //Used only by internal version.
50 unsigned logical; //Logical controller. Internal only.
51 std::string name; //Name. Internal only.
53 struct controller_double
55 wxPanel* panel;
56 wxStaticBox* box;
57 wxStaticText* label;
58 wxSizer* rtop;
59 wxSizer* top;
60 wxSizer* grid;
62 emulator_instance& inst;
63 std::map<int, control_triple> autoholds;
64 std::vector<controller_double> panels;
65 void update_controls();
66 bool closing;
67 wxBoxSizer* hsizer;
70 namespace
72 wxeditor_autohold* autohold_open;
75 wxeditor_autohold::~wxeditor_autohold() throw() {}
77 wxeditor_autohold::wxeditor_autohold(wxWindow* parent, emulator_instance& _inst)
78 : wxDialog(parent, wxID_ANY, wxT("lsnes: Autohold/Autofire"), wxDefaultPosition, wxSize(-1, -1)),
79 inst(_inst)
81 CHECK_UI_THREAD;
82 closing = false;
83 Centre();
84 hsizer = new wxBoxSizer(wxHORIZONTAL);
85 SetSizer(hsizer);
86 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_autohold::on_wclose));
87 update_controls();
88 hsizer->SetSizeHints(this);
89 Fit();
91 ahupdate.set(inst.dispatch->autohold_update, [this](unsigned port, unsigned controller,
92 unsigned ctrlnum, bool newstate) {
93 runuifun([this, port, controller, ctrlnum, newstate]() {
94 CHECK_UI_THREAD;
95 for(auto i : this->autoholds) {
96 if(i.second.port != port) continue;
97 if(i.second.controller != controller) continue;
98 if(i.second.index != ctrlnum) continue;
99 i.second.check->SetValue(newstate);
103 afupdate.set(inst.dispatch->autofire_update, [this](unsigned port, unsigned controller,
104 unsigned ctrlnum, unsigned duty, unsigned cyclelen) {
105 runuifun([this, port, controller, ctrlnum, duty]() {
106 CHECK_UI_THREAD;
107 for(auto i : this->autoholds) {
108 if(i.second.port != port) continue;
109 if(i.second.controller != controller) continue;
110 if(i.second.index != ctrlnum) continue;
111 i.second.afcheck->SetValue(duty != 0);
115 ahreconfigure.set(inst.dispatch->autohold_reconfigure, [this]() {
116 runuifun([this]() {
117 CHECK_UI_THREAD;
118 try {
119 this->update_controls();
120 } catch(std::runtime_error& e) {
121 //Close the window.
122 bool wasc = closing;
123 closing = true;
124 autohold_open = NULL;
125 if(!wasc)
126 Destroy();
132 void wxeditor_autohold::on_checkbox(wxCommandEvent& e)
134 CHECK_UI_THREAD;
135 int id = e.GetId();
136 if(!autoholds.count(id))
137 return;
138 auto t = autoholds[id];
139 bool isaf = (t.afid == id);
140 bool newstate = isaf ? t.afcheck->IsChecked() : t.check->IsChecked();
141 bool state = false;
142 inst.iqueue->run([t, newstate, &state, isaf]() {
143 auto& core = CORE();
144 if(isaf) {
145 auto _state = core.controls->autofire2(t.port, t.controller, t.index);
146 state = (_state.first != 0);
147 if(core.lua2->callback_do_button(t.port, t.controller, t.index, newstate ? "autofire 1 2" :
148 "autofire"))
149 return;
150 core.controls->autofire2(t.port, t.controller, t.index, newstate ? 1 : 0, newstate ? 2 : 1);
151 state = newstate;
152 } else {
153 state = core.controls->autohold2(t.port, t.controller, t.index);
154 if(core.lua2->callback_do_button(t.port, t.controller, t.index, newstate ? "hold" : "unhold"))
155 return;
156 core.controls->autohold2(t.port, t.controller, t.index, newstate);
157 state = newstate;
160 if(isaf)
161 t.afcheck->SetValue(state);
162 else
163 t.check->SetValue(state);
166 void wxeditor_autohold::update_controls()
168 CHECK_UI_THREAD;
169 for(auto i : autoholds) {
170 if(i.first != i.second.afid)
171 i.second.label->Destroy();
172 if(i.first != i.second.afid)
173 i.second.check->Destroy();
174 if(i.first == i.second.afid)
175 i.second.afcheck->Destroy();
177 for(auto i : panels) {
178 hsizer->Detach(i.panel);
179 i.panel->Destroy();
181 autoholds.clear();
182 panels.clear();
183 std::vector<control_triple> _autoholds;
184 std::vector<std::string> _controller_labels;
185 inst.iqueue->run([&_autoholds, &_controller_labels](){
186 auto& core = CORE();
187 std::map<std::string, unsigned> next_in_class;
188 portctrl::frame model = core.controls->get_blank();
189 const portctrl::type_set& pts = model.porttypes();
190 unsigned cnum_g = 0;
191 for(unsigned i = 0;; i++) {
192 auto pcid = core.controls->lcid_to_pcid(i);
193 if(pcid.first < 0)
194 break;
195 const portctrl::type& pt = pts.port_type(pcid.first);
196 const portctrl::controller_set& pci = *(pt.controller_info);
197 if((ssize_t)pci.controllers.size() <= pcid.second)
198 continue;
199 const portctrl::controller& pc = pci.controllers[pcid.second];
200 //First check that this has non-hidden buttons.
201 bool has_buttons = false;
202 for(unsigned k = 0; k < pc.buttons.size(); k++) {
203 const portctrl::button& pcb = pc.buttons[k];
204 if(pcb.type == portctrl::button::TYPE_BUTTON && !pcb.shadow)
205 has_buttons = true;
207 if(!has_buttons)
208 continue;
209 //Okay, a valid controller.
210 if(!next_in_class.count(pc.cclass))
211 next_in_class[pc.cclass] = 1;
212 uint32_t cnum = next_in_class[pc.cclass]++;
213 _controller_labels.push_back((stringfmt() << pc.cclass << "-" << cnum).str());
214 for(unsigned k = 0; k < pc.buttons.size(); k++) {
215 const portctrl::button& pcb = pc.buttons[k];
216 if(pcb.type != portctrl::button::TYPE_BUTTON || pcb.shadow)
217 continue;
218 struct control_triple t;
219 t.port = pcid.first;
220 t.controller = pcid.second;
221 t.index = k;
222 t.status = core.controls->autohold2(pcid.first, pcid.second, k);
223 auto h = core.controls->autofire2(pcid.first, pcid.second, k);
224 t.afstatus = (h.first > 0);
225 t.logical = cnum_g;
226 t.name = pcb.name;
227 _autoholds.push_back(t);
229 cnum_g++;
232 int next_id = wxID_HIGHEST + 1;
233 unsigned last_logical = 0xFFFFFFFFUL;
234 wxSizer* current = NULL;
235 wxPanel* current_p = NULL;
236 wxSizer* current_t = NULL;
237 for(auto i : _autoholds) {
238 if(i.logical != last_logical) {
239 //New controller starts.
240 if(current_t) {
241 hsizer->Add(current_p);
242 current_t->SetSizeHints(current_p);
243 current_t->Fit(current_p);
245 controller_double d;
246 current_p = d.panel = new wxPanel(this, wxID_ANY);
247 current_t = d.rtop = new wxBoxSizer(wxVERTICAL);
248 d.panel->SetSizer(d.rtop);
249 d.box = new wxStaticBox(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
250 d.top = new wxStaticBoxSizer(d.box, wxVERTICAL);
251 #ifdef __WXMAC__
252 d.label = new wxStaticText(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
253 d.top->Add(d.label);
254 #endif
255 current = d.grid = new wxFlexGridSizer(0, 3, 0, 0);
256 d.top->Add(d.grid);
257 d.rtop->Add(d.top);
258 panels.push_back(d);
259 last_logical = i.logical;
261 wxStaticText* label = new wxStaticText(current_p, wxID_ANY, towxstring(i.name));
262 wxCheckBox* check = new wxCheckBox(current_p, next_id, wxT("Hold"));
263 wxCheckBox* afcheck = new wxCheckBox(current_p, next_id + 1, wxT("Rapid"));
264 struct control_triple t;
265 t.port = i.port;
266 t.controller = i.controller;
267 t.index = i.index;
268 t.label = label;
269 t.check = check;
270 t.afcheck = afcheck;
271 t.afid = next_id + 1;
272 check->SetValue(i.status);
273 check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler(wxeditor_autohold::on_checkbox),
274 NULL, this);
275 afcheck->SetValue(i.afstatus);
276 afcheck->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
277 wxCommandEventHandler(wxeditor_autohold::on_checkbox), NULL, this);
278 current->Add(label);
279 current->Add(check);
280 current->Add(afcheck);
281 autoholds[next_id++] = t;
282 autoholds[next_id++] = t;
284 if(current_t) {
285 hsizer->Add(current_p);
286 current_t->SetSizeHints(current_p);
287 current_t->Fit(current_p);
289 if(_autoholds.empty()) {
290 throw std::runtime_error("No controlers");
292 hsizer->SetSizeHints(this);
293 hsizer->Layout();
294 hsizer->Fit(this);
295 Fit();
298 bool wxeditor_autohold::ShouldPreventAppExit() const { return false; }
300 void wxeditor_autohold::on_wclose(wxCloseEvent& e)
302 CHECK_UI_THREAD;
303 bool wasc = closing;
304 closing = true;
305 autohold_open = NULL;
306 if(!wasc)
307 Destroy();
310 void wxeditor_autohold_display(wxWindow* parent, emulator_instance& inst)
312 CHECK_UI_THREAD;
313 if(autohold_open)
314 return;
315 wxeditor_autohold* v;
316 try {
317 v = new wxeditor_autohold(parent, inst);
318 } catch(std::runtime_error& e) {
319 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
320 return;
322 v->Show();
323 autohold_open = v;