Make various instance stuff to take references to other instance objs
[lsnes.git] / src / platform / wxwidgets / editor-tasinput.cpp
blobd365ada14ec53a26ae059af4af2afa725482e93c
1 #include "core/command.hpp"
2 #include "core/controller.hpp"
3 #include "core/framebuffer.hpp"
4 #include "core/instance.hpp"
5 #include "core/movie.hpp"
6 #include "core/moviedata.hpp"
7 #include "core/dispatch.hpp"
8 #include "core/window.hpp"
10 #include "interface/controller.hpp"
11 #include "core/mainloop.hpp"
12 #include "platform/wxwidgets/platform.hpp"
13 #include "platform/wxwidgets/textrender.hpp"
14 #include "library/minmax.hpp"
15 #include "library/string.hpp"
16 #include "library/utf8.hpp"
18 #include <algorithm>
19 #include <cstring>
20 #include <wx/wx.h>
21 #include <wx/event.h>
22 #include <wx/control.h>
23 #include <wx/combobox.h>
24 #include <wx/statline.h>
25 #include <wx/spinctrl.h>
27 extern "C"
29 #ifndef UINT64_C
30 #define UINT64_C(val) val##ULL
31 #endif
32 #include <libswscale/swscale.h>
35 namespace
37 const char* button_codes = "QWERTYUIOPASDFGHJKLZXCVBNM";
39 std::string get_shorthand(int index) {
40 if(index < 0 || index > (int)strlen(button_codes))
41 return "";
42 return std::string(" [") + std::string(1, button_codes[index]) + "]";
45 const int padmajsize = 193;
46 const int padminsize = 16;
48 int32_t value_to_coordinate(int32_t rmin, int32_t rmax, int32_t val, int32_t dim)
50 if(dim == rmax - rmin + 1)
51 return val - rmin;
52 //Scale the values to be zero-based.
53 val = min(max(val, rmin), rmax);
54 rmax -= rmin;
55 val -= rmin;
56 int32_t center = rmax / 2;
57 int32_t cc = (dim - 1) / 2;
58 if(val == center)
59 return cc;
60 if(val < center) {
61 //0 => 0, center => cc.
62 return (val * (int64_t)cc + (center / 2)) / center;
64 if(val > center) {
65 //center => cc, rmax => dim - 1.
66 val -= center;
67 rmax -= center;
68 int32_t cc2 = (dim - 1 - cc);
69 return (val * (int64_t)cc2 + (rmax / 2)) / rmax + cc;
71 return 0; //NOTREACHED.
74 int32_t coordinate_to_value(int32_t rmin, int32_t rmax, int32_t val, int32_t dim)
76 if(dim == rmax - rmin + 1)
77 return val + rmin;
78 val = min(max(val, (int32_t)0), dim - 1);
79 int32_t center = (rmax + rmin) / 2;
80 int32_t cc = (dim - 1) / 2;
81 if(val == cc)
82 return center;
83 if(val < cc) {
84 //0 => rmin, cc => center.
85 return ((center - rmin) * (int64_t)val + cc / 2) / cc + rmin;
87 if(val > cc) {
88 //cc => center, dim - 1 => rmax.
89 uint32_t cc2 = (dim - 1 - cc);
90 return ((rmax - center) * (int64_t)(val - cc) + cc2 / 2) / cc2 + center;
92 return 0; //NOTREACHED.
96 class wxeditor_tasinput : public wxDialog
98 public:
99 wxeditor_tasinput(wxWindow* parent);
100 ~wxeditor_tasinput() throw();
101 bool ShouldPreventAppExit() const;
102 void on_wclose(wxCloseEvent& e);
103 void on_control(wxCommandEvent& e);
104 void on_keyboard_up(wxKeyEvent& e);
105 void on_keyboard_down(wxKeyEvent& e);
106 void call_screen_update();
107 private:
108 struct dispatch::target<> ahreconfigure;
109 struct xypanel;
110 struct control_triple
112 unsigned port;
113 unsigned controller;
114 unsigned xindex;
115 unsigned yindex; //Used for XY.
116 enum port_controller_button::_type type;
117 short xmin;
118 short ymin;
119 short xmax;
120 short ymax;
121 bool xcenter;
122 bool ycenter;
123 xypanel* panel;
124 wxCheckBox* check;
125 unsigned logical; //Logical controller. Internal only.
126 std::string name; //Name. Internal only.
128 struct controller_double
130 wxPanel* panel;
131 wxStaticBox* box;
132 wxStaticText* label;
133 wxSizer* rtop;
134 wxSizer* top;
136 struct xypanel : public wxEvtHandler
138 xypanel(wxWindow* win, wxSizer* s, control_triple _t, wxEvtHandler* _obj, wxObjectEventFunction _fun,
139 int _wxid);
140 ~xypanel();
141 short get_x() { return x; }
142 short get_y() { return y; }
143 void on_click(wxMouseEvent& e);
144 void on_numbers_change(wxSpinEvent& e);
145 void on_paint(wxPaintEvent& e);
146 void Destroy();
147 void do_redraw();
148 private:
149 friend class wxeditor_tasinput;
150 short x, y;
151 wxEvtHandler* obj;
152 wxObjectEventFunction fun;
153 int wxid;
154 control_triple t;
155 wxPanel* graphics;
156 wxSpinCtrl* xnum;
157 wxSpinCtrl* ynum;
158 bool lightgun;
159 bool dirty;
160 struct SwsContext* rctx;
161 int xstep, ystep;
163 std::map<int, control_triple> inputs;
164 std::vector<controller_double> panels;
165 void update_controls();
166 void connect_keyboard_recursive(wxWindow* win);
167 control_triple* find_triple(unsigned controller, unsigned control);
168 bool closing;
169 unsigned current_controller;
170 unsigned current_button;
171 wxBoxSizer* hsizer;
174 namespace
176 wxeditor_tasinput* tasinput_open;
179 wxeditor_tasinput::xypanel::xypanel(wxWindow* win, wxSizer* s, control_triple _t, wxEvtHandler* _obj,
180 wxObjectEventFunction _fun, int _wxid)
182 x = 0;
183 y = 0;
184 xnum = NULL;
185 ynum = NULL;
186 rctx = NULL;
187 xstep = 1;
188 ystep = 1;
189 dirty = false;
190 obj = _obj;
191 fun = _fun;
192 wxid = _wxid;
193 t = _t;
194 s->Add(new wxStaticText(win, wxID_ANY, towxstring(t.name), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS));
195 s->Add(graphics = new wxPanel(win, wxID_ANY));
196 lightgun = false;
197 if(t.type == port_controller_button::TYPE_LIGHTGUN && t.yindex != std::numeric_limits<unsigned>::max()) {
198 graphics->SetSize(t.xmax - t.xmin + 1, t.ymax - t.ymin + 1);
199 lightgun = true;
200 } else
201 graphics->SetSize(padmajsize, (t.yindex != std::numeric_limits<unsigned>::max()) ? padmajsize :
202 padminsize);
203 graphics->SetMinSize(graphics->GetSize());
204 graphics->SetMaxSize(graphics->GetSize());
205 graphics->Connect(wxEVT_PAINT, wxPaintEventHandler(xypanel::on_paint), NULL, this);
206 graphics->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(xypanel::on_click), NULL, this);
207 graphics->Connect(wxEVT_MOTION, wxMouseEventHandler(xypanel::on_click), NULL, this);
208 x = t.xcenter ? ((int)t.xmin + t.xmax) / 2 : t.xmin;
209 y = t.ycenter ? ((int)t.ymin + t.ymax) / 2 : t.ymin;
210 s->Add(xnum = new wxSpinCtrl(win, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS,
211 t.xmin, t.xmax, x));
212 if(t.yindex != std::numeric_limits<unsigned>::max())
213 s->Add(ynum = new wxSpinCtrl(win, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
214 wxSP_ARROW_KEYS | wxWANTS_CHARS, t.ymin, t.ymax, y));
215 xnum->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(xypanel::on_numbers_change), NULL, this);
216 if(ynum) ynum->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(xypanel::on_numbers_change), NULL,
217 this);
220 wxeditor_tasinput::xypanel::~xypanel()
222 sws_freeContext(rctx);
225 void wxeditor_tasinput::call_screen_update()
227 for(auto i : inputs)
228 if(i.second.type == port_controller_button::TYPE_LIGHTGUN)
229 i.second.panel->do_redraw();
232 void wxeditor_tasinput::xypanel::on_click(wxMouseEvent& e)
234 if(!e.Dragging() && !e.LeftDown())
235 return;
236 wxCommandEvent e2(0, wxid);
237 wxSize ps = graphics->GetSize();
238 x = coordinate_to_value(t.xmin, t.xmax, e.GetX(), ps.GetWidth());
239 y = coordinate_to_value(t.ymin, t.ymax, e.GetY(), ps.GetHeight());
240 if(xnum) xnum->SetValue(x);
241 if(ynum) ynum->SetValue(y);
242 do_redraw();
243 (obj->*fun)(e2);
246 void wxeditor_tasinput::xypanel::on_numbers_change(wxSpinEvent& e)
248 wxCommandEvent e2(0, wxid);
249 if(xnum) x = xnum->GetValue();
250 if(ynum) y = ynum->GetValue();
251 do_redraw();
252 (obj->*fun)(e2);
255 void wxeditor_tasinput::xypanel::do_redraw()
257 if(!dirty) {
258 dirty = true;
259 graphics->Refresh();
263 void wxeditor_tasinput::xypanel::on_paint(wxPaintEvent& e)
265 wxPaintDC dc(graphics);
266 if(lightgun) {
267 //Draw the current screen.
268 framebuffer::raw& _fb = lsnes_instance.fbuf.render_get_latest_screen();
269 framebuffer::fb<false> fb;
270 auto osize = std::make_pair(_fb.get_width(), _fb.get_height());
271 auto size = our_rom.rtype->lightgun_scale();
272 fb.reallocate(osize.first, osize.second, false);
273 fb.copy_from(_fb, 1, 1);
274 lsnes_instance.fbuf.render_get_latest_screen_end();
275 std::vector<uint8_t> buf;
276 buf.resize(3 * (t.xmax - t.xmin + 1) * (t.ymax - t.ymin + 1));
277 unsigned offX = -t.xmin;
278 unsigned offY = -t.ymin;
279 rctx = sws_getCachedContext(rctx, osize.first, osize.second, PIX_FMT_RGBA,
280 size.first, size.second, PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);
281 uint8_t* srcp[1];
282 int srcs[1];
283 uint8_t* dstp[1];
284 int dsts[1];
285 srcs[0] = 4 * (fb.rowptr(1) - fb.rowptr(0));
286 dsts[0] = 3 * (t.xmax - t.xmin + 1);
287 srcp[0] = reinterpret_cast<unsigned char*>(fb.rowptr(0));
288 dstp[0] = &buf[3 * (offY * (t.xmax - t.xmin + 1) + offX)];
289 memset(&buf[0], 0, buf.size());
290 sws_scale(rctx, srcp, srcs, 0, size.second, dstp, dsts);
291 wxBitmap bmp(wxImage(t.xmax - t.xmin + 1, t.ymax - t.ymin + 1, &buf[0], true));
292 dc.DrawBitmap(bmp, 0, 0, false);
293 } else {
294 dc.SetBackground(*wxWHITE_BRUSH);
295 dc.SetPen(*wxBLACK_PEN);
296 dc.Clear();
298 wxSize ps = graphics->GetSize();
299 dc.DrawLine(0, 0, ps.GetWidth(), 0);
300 dc.DrawLine(0, 0, 0, ps.GetHeight());
301 dc.DrawLine(0, ps.GetHeight() - 1, ps.GetWidth(), ps.GetHeight() - 1);
302 dc.DrawLine(ps.GetWidth() - 1, 0, ps.GetWidth() - 1, ps.GetHeight());
303 int xcenter = (ps.GetWidth() - 1) / 2;
304 int ycenter = (ps.GetHeight() - 1) / 2;
305 if(t.xcenter)
306 dc.DrawLine(xcenter, 0, xcenter, ps.GetHeight());
307 if((t.yindex != std::numeric_limits<unsigned>::max()) && t.ycenter)
308 dc.DrawLine(0, ycenter, ps.GetWidth(), ycenter);
309 dc.SetPen(*wxRED_PEN);
310 int xdraw = value_to_coordinate(t.xmin, t.xmax, x, ps.GetWidth());
311 int ydraw = value_to_coordinate(t.ymin, t.ymax, y, ps.GetHeight());
312 dc.DrawLine(xdraw, 0, xdraw, ps.GetHeight());
313 if((t.yindex != std::numeric_limits<unsigned>::max()) || t.ycenter)
314 dc.DrawLine(0, ydraw, ps.GetWidth(), ydraw);
315 dirty = false;
318 void wxeditor_tasinput::xypanel::Destroy()
320 graphics->Destroy();
321 xnum->Destroy();
322 if(ynum) ynum->Destroy();
325 wxeditor_tasinput::~wxeditor_tasinput() throw() {}
327 wxeditor_tasinput::wxeditor_tasinput(wxWindow* parent)
328 : wxDialog(parent, wxID_ANY, wxT("lsnes: TAS input plugin"), wxDefaultPosition, wxSize(-1, -1))
330 current_controller = 0;
331 current_button = 0;
332 closing = false;
333 Centre();
334 hsizer = new wxBoxSizer(wxHORIZONTAL);
335 SetSizer(hsizer);
336 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_tasinput::on_wclose));
337 update_controls();
338 hsizer->SetSizeHints(this);
339 Fit();
341 ahreconfigure.set(notify_autohold_reconfigure, [this]() {
342 runuifun([this]() {
343 try {
344 this->update_controls();
345 } catch(std::runtime_error& e) {
346 //Close the window.
347 bool wasc = closing;
348 closing = true;
349 tasinput_open = NULL;
350 lsnes_instance.controls.tasinput_enable(false);
351 if(!wasc)
352 Destroy();
358 void wxeditor_tasinput::connect_keyboard_recursive(wxWindow* win)
360 win->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxeditor_tasinput::on_keyboard_down), NULL, this);
361 win->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxeditor_tasinput::on_keyboard_up), NULL, this);
362 auto i = win->GetChildren().GetFirst();
363 while(i) {
364 connect_keyboard_recursive(i->GetData());
365 i = i->GetNext();
369 void wxeditor_tasinput::on_control(wxCommandEvent& e)
371 int id = e.GetId();
372 if(!inputs.count(id))
373 return;
374 auto t = inputs[id];
375 int16_t xstate = 0;
376 int16_t ystate = 0;
377 if(t.check)
378 xstate = t.check->GetValue() ? 1 : 0;
379 else if(t.panel) {
380 xstate = t.panel->get_x();
381 ystate = t.panel->get_y();
383 lsnes_instance.iqueue.run_async([t, xstate, ystate]() {
384 lsnes_instance.controls.tasinput(t.port, t.controller, t.xindex, xstate);
385 if(t.yindex != std::numeric_limits<unsigned>::max())
386 lsnes_instance.controls.tasinput(t.port, t.controller, t.yindex, ystate);
387 }, [](std::exception& e) {});
390 void wxeditor_tasinput::update_controls()
392 for(auto i : inputs) {
393 if(i.second.check)
394 i.second.check->Destroy();
395 if(i.second.panel)
396 i.second.panel->Destroy();
398 for(auto i : panels) {
399 hsizer->Detach(i.panel);
400 i.panel->Destroy();
402 inputs.clear();
403 panels.clear();
405 std::vector<control_triple> _inputs;
406 std::vector<std::string> _controller_labels;
407 lsnes_instance.iqueue.run([&_inputs, &_controller_labels](){
408 std::map<std::string, unsigned> next_in_class;
409 controller_frame model = CORE().controls.get_blank();
410 const port_type_set& pts = model.porttypes();
411 unsigned cnum_g = 0;
412 for(unsigned i = 0;; i++) {
413 auto pcid = CORE().controls.lcid_to_pcid(i);
414 if(pcid.first < 0)
415 break;
416 const port_type& pt = pts.port_type(pcid.first);
417 const port_controller_set& pci = *(pt.controller_info);
418 if((ssize_t)pci.controllers.size() <= pcid.second)
419 continue;
420 const port_controller& pc = pci.controllers[pcid.second];
421 //First check that this has non-hidden stuff.
422 bool has_buttons = false;
423 for(unsigned k = 0; k < pc.buttons.size(); k++) {
424 const port_controller_button& pcb = pc.buttons[k];
425 if(!pcb.shadow)
426 has_buttons = true;
428 if(!has_buttons)
429 continue;
430 //Okay, a valid controller.
431 if(!next_in_class.count(pc.cclass))
432 next_in_class[pc.cclass] = 1;
433 uint32_t cnum = next_in_class[pc.cclass]++;
434 _controller_labels.push_back((stringfmt() << pc.cclass << "-" << cnum).str());
435 //Go through all controller pairs.
436 for(unsigned k = 0; k < pc.analog_actions(); k++) {
437 std::pair<unsigned, unsigned> indices = pc.analog_action(k);
438 if(pc.buttons[indices.first].shadow)
439 continue;
440 struct control_triple t;
441 t.port = pcid.first;
442 t.controller = pcid.second;
443 t.xindex = indices.first;
444 t.yindex = indices.second;
445 t.xmin = pc.buttons[t.xindex].rmin;
446 t.xmax = pc.buttons[t.xindex].rmax;
447 t.xcenter = pc.buttons[t.xindex].centers;
448 t.ymin = 0;
449 t.ymax = 1;
450 t.ycenter = false;
451 t.type = pc.buttons[t.xindex].type;
452 t.name = pc.buttons[t.xindex].name;
453 t.logical = cnum_g;
454 if(t.yindex != std::numeric_limits<unsigned>::max()) {
455 t.ymin = pc.buttons[t.yindex].rmin;
456 t.ymax = pc.buttons[t.yindex].rmax;
457 t.ycenter = pc.buttons[t.yindex].centers;
458 t.name = t.name + "/" + pc.buttons[t.yindex].name;
460 _inputs.push_back(t);
462 //Go through all buttons.
463 for(unsigned k = 0; k < pc.buttons.size(); k++) {
464 const port_controller_button& pcb = pc.buttons[k];
465 if(pcb.type != port_controller_button::TYPE_BUTTON || pcb.shadow)
466 continue;
467 struct control_triple t;
468 t.port = pcid.first;
469 t.controller = pcid.second;
470 t.xindex = k;
471 t.yindex = std::numeric_limits<unsigned>::max();
472 t.logical = cnum_g;
473 t.type = port_controller_button::TYPE_BUTTON;
474 t.name = pcb.name;
475 _inputs.push_back(t);
477 for(unsigned k = 0; k < pc.buttons.size(); k++) {
478 const port_controller_button& pcb = pc.buttons[k];
479 if(pcb.type == port_controller_button::TYPE_BUTTON || pcb.shadow)
480 continue;
481 CORE().controls.tasinput(pcid.first, pcid.second, k, pcb.centers ? ((int)pcb.rmin +
482 pcb.rmax) / 2 : pcb.rmin);
484 cnum_g++;
487 int next_id = wxID_HIGHEST + 1;
488 unsigned last_logical = 0xFFFFFFFFUL;
489 wxSizer* current = NULL;
490 wxPanel* current_p = NULL;
491 for(auto i : _inputs) {
492 if(i.logical != last_logical) {
493 //New controller starts.
494 controller_double d;
495 current_p = d.panel = new wxPanel(this, wxID_ANY);
496 d.rtop = new wxBoxSizer(wxVERTICAL);
497 d.panel->SetSizer(d.rtop);
498 d.box = new wxStaticBox(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
499 current = d.top = new wxStaticBoxSizer(d.box, wxVERTICAL);
500 #ifdef __WXMAC__
501 d.label = new wxStaticText(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
502 d.top->Add(d.label);
503 #endif
504 d.rtop->Add(d.top);
505 hsizer->Add(d.panel);
506 panels.push_back(d);
507 last_logical = i.logical;
509 struct control_triple t = i;
510 t.check = NULL;
511 t.panel = NULL;
512 if(i.type == port_controller_button::TYPE_BUTTON) {
513 t.check = new wxCheckBox(current_p, next_id, towxstring(i.name + get_shorthand(i.xindex)),
514 wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
515 current->Add(t.check);
516 t.check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
517 wxCommandEventHandler(wxeditor_tasinput::on_control), NULL, this);
518 } else {
519 t.panel = new xypanel(current_p, current, i, this,
520 wxCommandEventHandler(wxeditor_tasinput::on_control), next_id);
522 inputs[next_id++] = t;
524 if(_inputs.empty()) {
525 throw std::runtime_error("No controlers");
527 auto tx = find_triple(current_controller = 0, current_button = 0);
528 if(tx) {
529 if(tx->check) tx->check->SetFocus();
530 else tx->panel->xnum->SetFocus();
532 //Connect the keyboard.
533 hsizer->Layout();
534 connect_keyboard_recursive(this);
535 Fit();
538 bool wxeditor_tasinput::ShouldPreventAppExit() const { return false; }
540 void wxeditor_tasinput::on_wclose(wxCloseEvent& e)
542 bool wasc = closing;
543 closing = true;
544 tasinput_open = NULL;
545 lsnes_instance.controls.tasinput_enable(false);
546 if(!wasc)
547 Destroy();
550 void wxeditor_tasinput::on_keyboard_down(wxKeyEvent& e)
552 int key = e.GetKeyCode();
553 if(key == WXK_LEFT || key == WXK_RIGHT || key == WXK_UP || key == WXK_DOWN) {
554 //See if this is associated with a panel.
555 int delta = 1;
556 if(e.GetModifiers() & wxMOD_SHIFT) delta = 999999998;
557 if(e.GetModifiers() & wxMOD_CONTROL) delta = 999999999;
558 if(key == WXK_LEFT || key == WXK_UP) delta = -delta;
559 bool vertical = (key == WXK_UP || key == WXK_DOWN);
560 auto t = find_triple(current_controller, current_button);
561 if(t->panel) {
562 //Handle movement better if span is large.
563 auto ctrl = vertical ? t->panel->ynum : t->panel->xnum;
564 if(!ctrl)
565 return;
566 if(abs(delta) == 999999998) {
567 int& wstep = vertical ? t->panel->ystep : t->panel->xstep;
568 int range = ctrl->GetMax() - ctrl->GetMin();
569 delta = (delta / abs(delta)) * wstep;
570 wstep += sqrt(wstep);
571 if(wstep > range / 20)
572 wstep = range / 20;
574 if(abs(delta) == 999999999) {
575 int range = ctrl->GetMax() - ctrl->GetMin();
576 delta = (delta / abs(delta)) * range / 16;
578 ctrl->SetValue(min(max(ctrl->GetValue() + delta, ctrl->GetMin()), ctrl->GetMax()));
579 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
580 e.SetPosition(ctrl->GetValue());
581 t->panel->on_numbers_change(e);
583 return;
585 if(key == WXK_F5) lsnes_instance.iqueue.queue("+advance-frame");
588 void wxeditor_tasinput::on_keyboard_up(wxKeyEvent& e)
590 int key = e.GetKeyCode();
591 if(key == WXK_LEFT || key == WXK_RIGHT) {
592 auto t = find_triple(current_controller, current_button);
593 if(t && t->panel) t->panel->xstep = 1;
595 if(key == WXK_UP || key == WXK_DOWN) {
596 auto t = find_triple(current_controller, current_button);
597 if(t && t->panel) t->panel->ystep = 1;
599 if(key == WXK_TAB) {
600 //Reset speed.
601 auto t = find_triple(current_controller, current_button);
602 if(t && t->panel) t->panel->xstep = t->panel->ystep = 1;
604 if(e.GetModifiers() & wxMOD_SHIFT) {
605 if(current_controller)
606 current_controller--;
607 else
608 current_controller = panels.size() - 1;
609 } else {
610 current_controller++;
611 if(current_controller >= panels.size())
612 current_controller = 0;
614 //Hilight zero control (but don't change). If it is a panel, it is X of it.
615 current_button = 0;
616 t = find_triple(current_controller, 0);
617 if(t) {
618 if(t->check)
619 t->check->SetFocus();
620 else
621 t->panel->xnum->SetFocus();
623 return;
625 for(const char* b = button_codes; *b; b++) {
626 if(key == *b) {
627 //Reset speed.
628 auto t = find_triple(current_controller, current_button);
629 if(t && t->panel) t->panel->xstep = t->panel->ystep = 1;
631 unsigned bn = b - button_codes;
632 //Select (current_controller,bn).
633 t = find_triple(current_controller, bn);
634 if(!t) return;
635 if(t->check) {
636 //Focus and toggle the checkbox.
637 t->check->SetFocus();
638 t->check->SetValue(!t->check->GetValue());
639 //Emit fake event.
640 wxCommandEvent e(wxEVT_COMMAND_CHECKBOX_CLICKED, t->check->GetId());
641 on_control(e);
642 current_button = bn;
643 return;
644 } else if(bn == t->xindex) {
645 //Focus the associated X box.
646 t->panel->xnum->SetFocus();
647 current_button = bn;
648 return;
649 } else if(bn == t->yindex) {
650 //Focus the associated Y box.
651 t->panel->ynum->SetFocus();
652 current_button = bn;
653 return;
655 return;
658 if(key == '\b') {
659 auto t = find_triple(current_controller, current_button);
660 if(t && t->panel) {
661 //Zero this.
662 auto ctrl = t->panel->ynum;
663 if(ctrl)
664 ctrl->SetValue(min(max(0, ctrl->GetMin()), ctrl->GetMax()));
665 ctrl = t->panel->xnum;
666 ctrl->SetValue(min(max(0, ctrl->GetMin()), ctrl->GetMax()));
667 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
668 e.SetPosition(0);
669 t->panel->on_numbers_change(e);
672 if(key == ' ') {
673 //Toggle button.
674 auto t = find_triple(current_controller, current_button);
675 if(t && t->check) {
676 t->check->SetValue(!t->check->GetValue());
677 wxCommandEvent e(wxEVT_COMMAND_CHECKBOX_CLICKED, t->check->GetId());
678 on_control(e);
680 return;
682 if(key == WXK_RETURN) {
683 try {
684 auto t = find_triple(current_controller, current_button);
685 if(!t || !t->panel) return;
686 bool vertical = (current_button == t->yindex);
687 auto ctrl = vertical ? t->panel->ynum : t->panel->xnum;
688 std::string v = pick_text(this, "Enter coordinate", "Enter new coordinate value",
689 (stringfmt() << ctrl->GetValue()).str(), false);
690 int val = parse_value<int>(v);
691 ctrl->SetValue(min(max(val, ctrl->GetMin()), ctrl->GetMax()));
692 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
693 e.SetPosition(ctrl->GetValue());
694 t->panel->on_numbers_change(e);
695 } catch(...) {
696 return;
699 if(key == WXK_F1) lsnes_instance.iqueue.queue("cycle-jukebox-backward");
700 if(key == WXK_F2) lsnes_instance.iqueue.queue("cycle-jukebox-forward");
701 if(key == WXK_F3) lsnes_instance.iqueue.queue("save-jukebox");
702 if(key == WXK_F4) lsnes_instance.iqueue.queue("load-jukebox");
703 if(key == WXK_F5) lsnes_instance.iqueue.queue("-advance-frame");
706 wxeditor_tasinput::control_triple* wxeditor_tasinput::find_triple(unsigned controller, unsigned control)
708 for(auto& i : inputs) {
709 if(i.second.logical != controller)
710 continue;
711 if(i.second.xindex == control)
712 return &i.second;
713 if(i.second.yindex == control)
714 return &i.second;
716 return NULL;
719 void wxeditor_tasinput_display(wxWindow* parent)
721 if(tasinput_open)
722 return;
723 wxeditor_tasinput* v;
724 try {
725 v = new wxeditor_tasinput(parent);
726 } catch(std::runtime_error& e) {
727 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
728 return;
730 v->Show();
731 tasinput_open = v;
732 lsnes_instance.controls.tasinput_enable(true);
735 void wxwindow_tasinput_update()
737 if(tasinput_open)
738 tasinput_open->call_screen_update();