Instancefy currently loaded ROM
[lsnes.git] / src / platform / wxwidgets / editor-tasinput.cpp
blobb5d562cd30fd51a1ded6f13a592d8bf5f0efe669
1 #include "core/command.hpp"
2 #include "core/controller.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/instance.hpp"
5 #include "core/instance-map.hpp"
6 #include "core/movie.hpp"
7 #include "core/moviedata.hpp"
8 #include "core/dispatch.hpp"
9 #include "core/window.hpp"
11 #include "interface/controller.hpp"
12 #include "core/mainloop.hpp"
13 #include "platform/wxwidgets/platform.hpp"
14 #include "platform/wxwidgets/textrender.hpp"
15 #include "library/minmax.hpp"
16 #include "library/string.hpp"
17 #include "library/utf8.hpp"
19 #include <algorithm>
20 #include <cstring>
21 #include <wx/wx.h>
22 #include <wx/event.h>
23 #include <wx/control.h>
24 #include <wx/combobox.h>
25 #include <wx/statline.h>
26 #include <wx/spinctrl.h>
28 extern "C"
30 #ifndef UINT64_C
31 #define UINT64_C(val) val##ULL
32 #endif
33 #include <libswscale/swscale.h>
36 namespace
38 const char* button_codes = "QWERTYUIOPASDFGHJKLZXCVBNM";
40 std::string get_shorthand(int index) {
41 if(index < 0 || index > (int)strlen(button_codes))
42 return "";
43 return std::string(" [") + std::string(1, button_codes[index]) + "]";
46 const int padmajsize = 193;
47 const int padminsize = 16;
49 int32_t value_to_coordinate(int32_t rmin, int32_t rmax, int32_t val, int32_t dim)
51 if(dim == rmax - rmin + 1)
52 return val - rmin;
53 //Scale the values to be zero-based.
54 val = min(max(val, rmin), rmax);
55 rmax -= rmin;
56 val -= rmin;
57 int32_t center = rmax / 2;
58 int32_t cc = (dim - 1) / 2;
59 if(val == center)
60 return cc;
61 if(val < center) {
62 //0 => 0, center => cc.
63 return (val * (int64_t)cc + (center / 2)) / center;
65 if(val > center) {
66 //center => cc, rmax => dim - 1.
67 val -= center;
68 rmax -= center;
69 int32_t cc2 = (dim - 1 - cc);
70 return (val * (int64_t)cc2 + (rmax / 2)) / rmax + cc;
72 return 0; //NOTREACHED.
75 int32_t coordinate_to_value(int32_t rmin, int32_t rmax, int32_t val, int32_t dim)
77 if(dim == rmax - rmin + 1)
78 return val + rmin;
79 val = min(max(val, (int32_t)0), dim - 1);
80 int32_t center = (rmax + rmin) / 2;
81 int32_t cc = (dim - 1) / 2;
82 if(val == cc)
83 return center;
84 if(val < cc) {
85 //0 => rmin, cc => center.
86 return ((center - rmin) * (int64_t)val + cc / 2) / cc + rmin;
88 if(val > cc) {
89 //cc => center, dim - 1 => rmax.
90 uint32_t cc2 = (dim - 1 - cc);
91 return ((rmax - center) * (int64_t)(val - cc) + cc2 / 2) / cc2 + center;
93 return 0; //NOTREACHED.
97 class wxeditor_tasinput : public wxDialog
99 public:
100 wxeditor_tasinput(emulator_instance& _inst, wxWindow* parent);
101 ~wxeditor_tasinput() throw();
102 bool ShouldPreventAppExit() const;
103 void on_wclose(wxCloseEvent& e);
104 void on_control(wxCommandEvent& e);
105 void on_keyboard_up(wxKeyEvent& e);
106 void on_keyboard_down(wxKeyEvent& e);
107 void call_screen_update();
108 private:
109 struct dispatch::target<> ahreconfigure;
110 struct xypanel;
111 struct control_triple
113 unsigned port;
114 unsigned controller;
115 unsigned xindex;
116 unsigned yindex; //Used for XY.
117 enum port_controller_button::_type type;
118 short xmin;
119 short ymin;
120 short xmax;
121 short ymax;
122 bool xcenter;
123 bool ycenter;
124 xypanel* panel;
125 wxCheckBox* check;
126 unsigned logical; //Logical controller. Internal only.
127 std::string name; //Name. Internal only.
129 struct controller_double
131 wxPanel* panel;
132 wxStaticBox* box;
133 wxStaticText* label;
134 wxSizer* rtop;
135 wxSizer* top;
137 struct xypanel : public wxEvtHandler
139 xypanel(wxWindow* win, emulator_instance& _inst, wxSizer* s, control_triple _t, wxEvtHandler* _obj,
140 wxObjectEventFunction _fun, int _wxid);
141 ~xypanel();
142 short get_x() { return x; }
143 short get_y() { return y; }
144 void on_click(wxMouseEvent& e);
145 void on_numbers_change(wxSpinEvent& e);
146 void on_paint(wxPaintEvent& e);
147 void Destroy();
148 void do_redraw();
149 private:
150 friend class wxeditor_tasinput;
151 emulator_instance& inst;
152 short x, y;
153 wxEvtHandler* obj;
154 wxObjectEventFunction fun;
155 int wxid;
156 control_triple t;
157 wxPanel* graphics;
158 wxSpinCtrl* xnum;
159 wxSpinCtrl* ynum;
160 bool lightgun;
161 bool dirty;
162 struct SwsContext* rctx;
163 int xstep, ystep;
165 emulator_instance& inst;
166 std::map<int, control_triple> inputs;
167 std::vector<controller_double> panels;
168 void update_controls();
169 void connect_keyboard_recursive(wxWindow* win);
170 control_triple* find_triple(unsigned controller, unsigned control);
171 bool closing;
172 unsigned current_controller;
173 unsigned current_button;
174 wxBoxSizer* hsizer;
177 namespace
179 instance_map<wxeditor_tasinput> tasinputs;
182 wxeditor_tasinput::xypanel::xypanel(wxWindow* win, emulator_instance& _inst, wxSizer* s, control_triple _t,
183 wxEvtHandler* _obj, wxObjectEventFunction _fun, int _wxid)
184 : inst(_inst)
186 x = 0;
187 y = 0;
188 xnum = NULL;
189 ynum = NULL;
190 rctx = NULL;
191 xstep = 1;
192 ystep = 1;
193 dirty = false;
194 obj = _obj;
195 fun = _fun;
196 wxid = _wxid;
197 t = _t;
198 s->Add(new wxStaticText(win, wxID_ANY, towxstring(t.name), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS));
199 s->Add(graphics = new wxPanel(win, wxID_ANY));
200 lightgun = false;
201 if(t.type == port_controller_button::TYPE_LIGHTGUN && t.yindex != std::numeric_limits<unsigned>::max()) {
202 graphics->SetSize(t.xmax - t.xmin + 1, t.ymax - t.ymin + 1);
203 lightgun = true;
204 } else
205 graphics->SetSize(padmajsize, (t.yindex != std::numeric_limits<unsigned>::max()) ? padmajsize :
206 padminsize);
207 graphics->SetMinSize(graphics->GetSize());
208 graphics->SetMaxSize(graphics->GetSize());
209 graphics->Connect(wxEVT_PAINT, wxPaintEventHandler(xypanel::on_paint), NULL, this);
210 graphics->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(xypanel::on_click), NULL, this);
211 graphics->Connect(wxEVT_MOTION, wxMouseEventHandler(xypanel::on_click), NULL, this);
212 x = t.xcenter ? ((int)t.xmin + t.xmax) / 2 : t.xmin;
213 y = t.ycenter ? ((int)t.ymin + t.ymax) / 2 : t.ymin;
214 s->Add(xnum = new wxSpinCtrl(win, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS,
215 t.xmin, t.xmax, x));
216 if(t.yindex != std::numeric_limits<unsigned>::max())
217 s->Add(ynum = new wxSpinCtrl(win, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
218 wxSP_ARROW_KEYS | wxWANTS_CHARS, t.ymin, t.ymax, y));
219 xnum->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(xypanel::on_numbers_change), NULL, this);
220 if(ynum) ynum->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(xypanel::on_numbers_change), NULL,
221 this);
224 wxeditor_tasinput::xypanel::~xypanel()
226 sws_freeContext(rctx);
229 void wxeditor_tasinput::call_screen_update()
231 for(auto i : inputs)
232 if(i.second.type == port_controller_button::TYPE_LIGHTGUN)
233 i.second.panel->do_redraw();
236 void wxeditor_tasinput::xypanel::on_click(wxMouseEvent& e)
238 if(!e.Dragging() && !e.LeftDown())
239 return;
240 wxCommandEvent e2(0, wxid);
241 wxSize ps = graphics->GetSize();
242 x = coordinate_to_value(t.xmin, t.xmax, e.GetX(), ps.GetWidth());
243 y = coordinate_to_value(t.ymin, t.ymax, e.GetY(), ps.GetHeight());
244 if(xnum) xnum->SetValue(x);
245 if(ynum) ynum->SetValue(y);
246 do_redraw();
247 (obj->*fun)(e2);
250 void wxeditor_tasinput::xypanel::on_numbers_change(wxSpinEvent& e)
252 wxCommandEvent e2(0, wxid);
253 if(xnum) x = xnum->GetValue();
254 if(ynum) y = ynum->GetValue();
255 do_redraw();
256 (obj->*fun)(e2);
259 void wxeditor_tasinput::xypanel::do_redraw()
261 if(!dirty) {
262 dirty = true;
263 graphics->Refresh();
267 void wxeditor_tasinput::xypanel::on_paint(wxPaintEvent& e)
269 wxPaintDC dc(graphics);
270 if(lightgun) {
271 //Draw the current screen.
272 framebuffer::raw& _fb = inst.fbuf->render_get_latest_screen();
273 framebuffer::fb<false> fb;
274 auto osize = std::make_pair(_fb.get_width(), _fb.get_height());
275 auto size = inst.rom->rtype->lightgun_scale();
276 fb.reallocate(osize.first, osize.second, false);
277 fb.copy_from(_fb, 1, 1);
278 inst.fbuf->render_get_latest_screen_end();
279 std::vector<uint8_t> buf;
280 buf.resize(3 * (t.xmax - t.xmin + 1) * (t.ymax - t.ymin + 1));
281 unsigned offX = -t.xmin;
282 unsigned offY = -t.ymin;
283 rctx = sws_getCachedContext(rctx, osize.first, osize.second, PIX_FMT_RGBA,
284 size.first, size.second, PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);
285 uint8_t* srcp[1];
286 int srcs[1];
287 uint8_t* dstp[1];
288 int dsts[1];
289 srcs[0] = 4 * (fb.rowptr(1) - fb.rowptr(0));
290 dsts[0] = 3 * (t.xmax - t.xmin + 1);
291 srcp[0] = reinterpret_cast<unsigned char*>(fb.rowptr(0));
292 dstp[0] = &buf[3 * (offY * (t.xmax - t.xmin + 1) + offX)];
293 memset(&buf[0], 0, buf.size());
294 sws_scale(rctx, srcp, srcs, 0, size.second, dstp, dsts);
295 wxBitmap bmp(wxImage(t.xmax - t.xmin + 1, t.ymax - t.ymin + 1, &buf[0], true));
296 dc.DrawBitmap(bmp, 0, 0, false);
297 } else {
298 dc.SetBackground(*wxWHITE_BRUSH);
299 dc.SetPen(*wxBLACK_PEN);
300 dc.Clear();
302 wxSize ps = graphics->GetSize();
303 dc.DrawLine(0, 0, ps.GetWidth(), 0);
304 dc.DrawLine(0, 0, 0, ps.GetHeight());
305 dc.DrawLine(0, ps.GetHeight() - 1, ps.GetWidth(), ps.GetHeight() - 1);
306 dc.DrawLine(ps.GetWidth() - 1, 0, ps.GetWidth() - 1, ps.GetHeight());
307 int xcenter = (ps.GetWidth() - 1) / 2;
308 int ycenter = (ps.GetHeight() - 1) / 2;
309 if(t.xcenter)
310 dc.DrawLine(xcenter, 0, xcenter, ps.GetHeight());
311 if((t.yindex != std::numeric_limits<unsigned>::max()) && t.ycenter)
312 dc.DrawLine(0, ycenter, ps.GetWidth(), ycenter);
313 dc.SetPen(*wxRED_PEN);
314 int xdraw = value_to_coordinate(t.xmin, t.xmax, x, ps.GetWidth());
315 int ydraw = value_to_coordinate(t.ymin, t.ymax, y, ps.GetHeight());
316 dc.DrawLine(xdraw, 0, xdraw, ps.GetHeight());
317 if((t.yindex != std::numeric_limits<unsigned>::max()) || t.ycenter)
318 dc.DrawLine(0, ydraw, ps.GetWidth(), ydraw);
319 dirty = false;
322 void wxeditor_tasinput::xypanel::Destroy()
324 graphics->Destroy();
325 xnum->Destroy();
326 if(ynum) ynum->Destroy();
329 wxeditor_tasinput::~wxeditor_tasinput() throw()
331 tasinputs.remove(this->inst);
334 wxeditor_tasinput::wxeditor_tasinput(emulator_instance& _inst, wxWindow* parent)
335 : wxDialog(parent, wxID_ANY, wxT("lsnes: TAS input plugin"), wxDefaultPosition, wxSize(-1, -1)),
336 inst(_inst)
338 current_controller = 0;
339 current_button = 0;
340 closing = false;
341 Centre();
342 hsizer = new wxBoxSizer(wxHORIZONTAL);
343 SetSizer(hsizer);
344 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_tasinput::on_wclose));
345 update_controls();
346 hsizer->SetSizeHints(this);
347 Fit();
349 ahreconfigure.set(inst.dispatch->autohold_reconfigure, [this]() {
350 runuifun([this]() {
351 try {
352 this->update_controls();
353 } catch(std::runtime_error& e) {
354 //Close the window.
355 bool wasc = closing;
356 closing = true;
357 inst.controls->tasinput_enable(false);
358 if(!wasc)
359 Destroy();
365 void wxeditor_tasinput::connect_keyboard_recursive(wxWindow* win)
367 win->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxeditor_tasinput::on_keyboard_down), NULL, this);
368 win->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxeditor_tasinput::on_keyboard_up), NULL, this);
369 auto i = win->GetChildren().GetFirst();
370 while(i) {
371 connect_keyboard_recursive(i->GetData());
372 i = i->GetNext();
376 void wxeditor_tasinput::on_control(wxCommandEvent& e)
378 int id = e.GetId();
379 if(!inputs.count(id))
380 return;
381 auto t = inputs[id];
382 int16_t xstate = 0;
383 int16_t ystate = 0;
384 if(t.check)
385 xstate = t.check->GetValue() ? 1 : 0;
386 else if(t.panel) {
387 xstate = t.panel->get_x();
388 ystate = t.panel->get_y();
390 inst.iqueue->run_async([t, xstate, ystate]() {
391 CORE().controls->tasinput(t.port, t.controller, t.xindex, xstate);
392 if(t.yindex != std::numeric_limits<unsigned>::max())
393 CORE().controls->tasinput(t.port, t.controller, t.yindex, ystate);
394 }, [](std::exception& e) {});
397 void wxeditor_tasinput::update_controls()
399 for(auto i : inputs) {
400 if(i.second.check)
401 i.second.check->Destroy();
402 if(i.second.panel)
403 i.second.panel->Destroy();
405 for(auto i : panels) {
406 hsizer->Detach(i.panel);
407 i.panel->Destroy();
409 inputs.clear();
410 panels.clear();
412 std::vector<control_triple> _inputs;
413 std::vector<std::string> _controller_labels;
414 inst.iqueue->run([&_inputs, &_controller_labels](){
415 std::map<std::string, unsigned> next_in_class;
416 controller_frame model = CORE().controls->get_blank();
417 const port_type_set& pts = model.porttypes();
418 unsigned cnum_g = 0;
419 for(unsigned i = 0;; i++) {
420 auto pcid = CORE().controls->lcid_to_pcid(i);
421 if(pcid.first < 0)
422 break;
423 const port_type& pt = pts.port_type(pcid.first);
424 const port_controller_set& pci = *(pt.controller_info);
425 if((ssize_t)pci.controllers.size() <= pcid.second)
426 continue;
427 const port_controller& pc = pci.controllers[pcid.second];
428 //First check that this has non-hidden stuff.
429 bool has_buttons = false;
430 for(unsigned k = 0; k < pc.buttons.size(); k++) {
431 const port_controller_button& pcb = pc.buttons[k];
432 if(!pcb.shadow)
433 has_buttons = true;
435 if(!has_buttons)
436 continue;
437 //Okay, a valid controller.
438 if(!next_in_class.count(pc.cclass))
439 next_in_class[pc.cclass] = 1;
440 uint32_t cnum = next_in_class[pc.cclass]++;
441 _controller_labels.push_back((stringfmt() << pc.cclass << "-" << cnum).str());
442 //Go through all controller pairs.
443 for(unsigned k = 0; k < pc.analog_actions(); k++) {
444 std::pair<unsigned, unsigned> indices = pc.analog_action(k);
445 if(pc.buttons[indices.first].shadow)
446 continue;
447 struct control_triple t;
448 t.port = pcid.first;
449 t.controller = pcid.second;
450 t.xindex = indices.first;
451 t.yindex = indices.second;
452 t.xmin = pc.buttons[t.xindex].rmin;
453 t.xmax = pc.buttons[t.xindex].rmax;
454 t.xcenter = pc.buttons[t.xindex].centers;
455 t.ymin = 0;
456 t.ymax = 1;
457 t.ycenter = false;
458 t.type = pc.buttons[t.xindex].type;
459 t.name = pc.buttons[t.xindex].name;
460 t.logical = cnum_g;
461 if(t.yindex != std::numeric_limits<unsigned>::max()) {
462 t.ymin = pc.buttons[t.yindex].rmin;
463 t.ymax = pc.buttons[t.yindex].rmax;
464 t.ycenter = pc.buttons[t.yindex].centers;
465 t.name = t.name + "/" + pc.buttons[t.yindex].name;
467 _inputs.push_back(t);
469 //Go through all buttons.
470 for(unsigned k = 0; k < pc.buttons.size(); k++) {
471 const port_controller_button& pcb = pc.buttons[k];
472 if(pcb.type != port_controller_button::TYPE_BUTTON || pcb.shadow)
473 continue;
474 struct control_triple t;
475 t.port = pcid.first;
476 t.controller = pcid.second;
477 t.xindex = k;
478 t.yindex = std::numeric_limits<unsigned>::max();
479 t.logical = cnum_g;
480 t.type = port_controller_button::TYPE_BUTTON;
481 t.name = pcb.name;
482 _inputs.push_back(t);
484 for(unsigned k = 0; k < pc.buttons.size(); k++) {
485 const port_controller_button& pcb = pc.buttons[k];
486 if(pcb.type == port_controller_button::TYPE_BUTTON || pcb.shadow)
487 continue;
488 CORE().controls->tasinput(pcid.first, pcid.second, k, pcb.centers ? ((int)pcb.rmin +
489 pcb.rmax) / 2 : pcb.rmin);
491 cnum_g++;
494 int next_id = wxID_HIGHEST + 1;
495 unsigned last_logical = 0xFFFFFFFFUL;
496 wxSizer* current = NULL;
497 wxPanel* current_p = NULL;
498 for(auto i : _inputs) {
499 if(i.logical != last_logical) {
500 //New controller starts.
501 controller_double d;
502 current_p = d.panel = new wxPanel(this, wxID_ANY);
503 d.rtop = new wxBoxSizer(wxVERTICAL);
504 d.panel->SetSizer(d.rtop);
505 d.box = new wxStaticBox(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
506 current = d.top = new wxStaticBoxSizer(d.box, wxVERTICAL);
507 #ifdef __WXMAC__
508 d.label = new wxStaticText(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
509 d.top->Add(d.label);
510 #endif
511 d.rtop->Add(d.top);
512 hsizer->Add(d.panel);
513 panels.push_back(d);
514 last_logical = i.logical;
516 struct control_triple t = i;
517 t.check = NULL;
518 t.panel = NULL;
519 if(i.type == port_controller_button::TYPE_BUTTON) {
520 t.check = new wxCheckBox(current_p, next_id, towxstring(i.name + get_shorthand(i.xindex)),
521 wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
522 current->Add(t.check);
523 t.check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
524 wxCommandEventHandler(wxeditor_tasinput::on_control), NULL, this);
525 } else {
526 t.panel = new xypanel(current_p, inst, current, i, this,
527 wxCommandEventHandler(wxeditor_tasinput::on_control), next_id);
529 inputs[next_id++] = t;
531 if(_inputs.empty()) {
532 throw std::runtime_error("No controlers");
534 auto tx = find_triple(current_controller = 0, current_button = 0);
535 if(tx) {
536 if(tx->check) tx->check->SetFocus();
537 else tx->panel->xnum->SetFocus();
539 //Connect the keyboard.
540 hsizer->Layout();
541 connect_keyboard_recursive(this);
542 Fit();
545 bool wxeditor_tasinput::ShouldPreventAppExit() const { return false; }
547 void wxeditor_tasinput::on_wclose(wxCloseEvent& e)
549 bool wasc = closing;
550 closing = true;
551 inst.controls->tasinput_enable(false);
552 if(!wasc)
553 Destroy();
556 void wxeditor_tasinput::on_keyboard_down(wxKeyEvent& e)
558 int key = e.GetKeyCode();
559 if(key == WXK_LEFT || key == WXK_RIGHT || key == WXK_UP || key == WXK_DOWN) {
560 //See if this is associated with a panel.
561 int delta = 1;
562 if(e.GetModifiers() & wxMOD_SHIFT) delta = 999999998;
563 if(e.GetModifiers() & wxMOD_CONTROL) delta = 999999999;
564 if(key == WXK_LEFT || key == WXK_UP) delta = -delta;
565 bool vertical = (key == WXK_UP || key == WXK_DOWN);
566 auto t = find_triple(current_controller, current_button);
567 if(t->panel) {
568 //Handle movement better if span is large.
569 auto ctrl = vertical ? t->panel->ynum : t->panel->xnum;
570 if(!ctrl)
571 return;
572 if(abs(delta) == 999999998) {
573 int& wstep = vertical ? t->panel->ystep : t->panel->xstep;
574 int range = ctrl->GetMax() - ctrl->GetMin();
575 delta = (delta / abs(delta)) * wstep;
576 wstep += sqrt(wstep);
577 if(wstep > range / 20)
578 wstep = range / 20;
580 if(abs(delta) == 999999999) {
581 int range = ctrl->GetMax() - ctrl->GetMin();
582 delta = (delta / abs(delta)) * range / 16;
584 ctrl->SetValue(min(max(ctrl->GetValue() + delta, ctrl->GetMin()), ctrl->GetMax()));
585 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
586 e.SetPosition(ctrl->GetValue());
587 t->panel->on_numbers_change(e);
589 return;
591 if(key == WXK_F5) inst.iqueue->queue("+advance-frame");
594 void wxeditor_tasinput::on_keyboard_up(wxKeyEvent& e)
596 int key = e.GetKeyCode();
597 if(key == WXK_LEFT || key == WXK_RIGHT) {
598 auto t = find_triple(current_controller, current_button);
599 if(t && t->panel) t->panel->xstep = 1;
601 if(key == WXK_UP || key == WXK_DOWN) {
602 auto t = find_triple(current_controller, current_button);
603 if(t && t->panel) t->panel->ystep = 1;
605 if(key == WXK_TAB) {
606 //Reset speed.
607 auto t = find_triple(current_controller, current_button);
608 if(t && t->panel) t->panel->xstep = t->panel->ystep = 1;
610 if(e.GetModifiers() & wxMOD_SHIFT) {
611 if(current_controller)
612 current_controller--;
613 else
614 current_controller = panels.size() - 1;
615 } else {
616 current_controller++;
617 if(current_controller >= panels.size())
618 current_controller = 0;
620 //Hilight zero control (but don't change). If it is a panel, it is X of it.
621 current_button = 0;
622 t = find_triple(current_controller, 0);
623 if(t) {
624 if(t->check)
625 t->check->SetFocus();
626 else
627 t->panel->xnum->SetFocus();
629 return;
631 for(const char* b = button_codes; *b; b++) {
632 if(key == *b) {
633 //Reset speed.
634 auto t = find_triple(current_controller, current_button);
635 if(t && t->panel) t->panel->xstep = t->panel->ystep = 1;
637 unsigned bn = b - button_codes;
638 //Select (current_controller,bn).
639 t = find_triple(current_controller, bn);
640 if(!t) return;
641 if(t->check) {
642 //Focus and toggle the checkbox.
643 t->check->SetFocus();
644 t->check->SetValue(!t->check->GetValue());
645 //Emit fake event.
646 wxCommandEvent e(wxEVT_COMMAND_CHECKBOX_CLICKED, t->check->GetId());
647 on_control(e);
648 current_button = bn;
649 return;
650 } else if(bn == t->xindex) {
651 //Focus the associated X box.
652 t->panel->xnum->SetFocus();
653 current_button = bn;
654 return;
655 } else if(bn == t->yindex) {
656 //Focus the associated Y box.
657 t->panel->ynum->SetFocus();
658 current_button = bn;
659 return;
661 return;
664 if(key == '\b') {
665 auto t = find_triple(current_controller, current_button);
666 if(t && t->panel) {
667 //Zero this.
668 auto ctrl = t->panel->ynum;
669 if(ctrl)
670 ctrl->SetValue(min(max(0, ctrl->GetMin()), ctrl->GetMax()));
671 ctrl = t->panel->xnum;
672 ctrl->SetValue(min(max(0, ctrl->GetMin()), ctrl->GetMax()));
673 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
674 e.SetPosition(0);
675 t->panel->on_numbers_change(e);
678 if(key == ' ') {
679 //Toggle button.
680 auto t = find_triple(current_controller, current_button);
681 if(t && t->check) {
682 t->check->SetValue(!t->check->GetValue());
683 wxCommandEvent e(wxEVT_COMMAND_CHECKBOX_CLICKED, t->check->GetId());
684 on_control(e);
686 return;
688 if(key == WXK_RETURN) {
689 try {
690 auto t = find_triple(current_controller, current_button);
691 if(!t || !t->panel) return;
692 bool vertical = (current_button == t->yindex);
693 auto ctrl = vertical ? t->panel->ynum : t->panel->xnum;
694 std::string v = pick_text(this, "Enter coordinate", "Enter new coordinate value",
695 (stringfmt() << ctrl->GetValue()).str(), false);
696 int val = parse_value<int>(v);
697 ctrl->SetValue(min(max(val, ctrl->GetMin()), ctrl->GetMax()));
698 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
699 e.SetPosition(ctrl->GetValue());
700 t->panel->on_numbers_change(e);
701 } catch(...) {
702 return;
705 if(key == WXK_F1) inst.iqueue->queue("cycle-jukebox-backward");
706 if(key == WXK_F2) inst.iqueue->queue("cycle-jukebox-forward");
707 if(key == WXK_F3) inst.iqueue->queue("save-jukebox");
708 if(key == WXK_F4) inst.iqueue->queue("load-jukebox");
709 if(key == WXK_F5) inst.iqueue->queue("-advance-frame");
712 wxeditor_tasinput::control_triple* wxeditor_tasinput::find_triple(unsigned controller, unsigned control)
714 for(auto& i : inputs) {
715 if(i.second.logical != controller)
716 continue;
717 if(i.second.xindex == control)
718 return &i.second;
719 if(i.second.yindex == control)
720 return &i.second;
722 return NULL;
725 void wxeditor_tasinput_display(wxWindow* parent, emulator_instance& inst)
727 auto e = tasinputs.lookup(inst);
728 if(e) {
729 e->Raise();
730 return;
732 wxeditor_tasinput* v;
733 try {
734 v = tasinputs.create(inst, parent);
735 } catch(std::runtime_error& e) {
736 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
737 return;
739 v->Show();
740 inst.controls->tasinput_enable(true);
743 void wxwindow_tasinput_update(emulator_instance& inst)
745 auto e = tasinputs.lookup(inst);
746 if(e) e->call_screen_update();