Fix Win32 build
[lsnes.git] / src / platform / wxwidgets / editor-tasinput.cpp
blobf1fcb142e5d528a8f2216d7b46e3bcba32972f99
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>
6 #include <wx/spinctrl.h>
8 #include "core/command.hpp"
9 #include "core/controller.hpp"
10 #include "core/framebuffer.hpp"
11 #include "core/instance.hpp"
12 #include "core/instance-map.hpp"
13 #include "core/movie.hpp"
14 #include "core/moviedata.hpp"
15 #include "core/dispatch.hpp"
16 #include "core/window.hpp"
18 #include "interface/controller.hpp"
19 #include "core/mainloop.hpp"
20 #include "platform/wxwidgets/platform.hpp"
21 #include "platform/wxwidgets/textrender.hpp"
22 #include "library/minmax.hpp"
23 #include "library/string.hpp"
24 #include "library/utf8.hpp"
26 #include <algorithm>
27 #include <cstring>
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 portctrl::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 CHECK_UI_THREAD;
187 x = 0;
188 y = 0;
189 xnum = NULL;
190 ynum = NULL;
191 rctx = NULL;
192 xstep = 1;
193 ystep = 1;
194 dirty = false;
195 obj = _obj;
196 fun = _fun;
197 wxid = _wxid;
198 t = _t;
199 s->Add(new wxStaticText(win, wxID_ANY, towxstring(t.name), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS));
200 s->Add(graphics = new wxPanel(win, wxID_ANY));
201 lightgun = false;
202 if(t.type == portctrl::button::TYPE_LIGHTGUN && t.yindex != std::numeric_limits<unsigned>::max()) {
203 graphics->SetSize(t.xmax - t.xmin + 1, t.ymax - t.ymin + 1);
204 lightgun = true;
205 } else
206 graphics->SetSize(padmajsize, (t.yindex != std::numeric_limits<unsigned>::max()) ? padmajsize :
207 padminsize);
208 graphics->SetMinSize(graphics->GetSize());
209 graphics->SetMaxSize(graphics->GetSize());
210 graphics->Connect(wxEVT_PAINT, wxPaintEventHandler(xypanel::on_paint), NULL, this);
211 graphics->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(xypanel::on_click), NULL, this);
212 graphics->Connect(wxEVT_MOTION, wxMouseEventHandler(xypanel::on_click), NULL, this);
213 x = t.xcenter ? ((int)t.xmin + t.xmax) / 2 : t.xmin;
214 y = t.ycenter ? ((int)t.ymin + t.ymax) / 2 : t.ymin;
215 s->Add(xnum = new wxSpinCtrl(win, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS,
216 t.xmin, t.xmax, x));
217 if(t.yindex != std::numeric_limits<unsigned>::max())
218 s->Add(ynum = new wxSpinCtrl(win, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize,
219 wxSP_ARROW_KEYS | wxWANTS_CHARS, t.ymin, t.ymax, y));
220 xnum->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(xypanel::on_numbers_change), NULL, this);
221 if(ynum) ynum->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler(xypanel::on_numbers_change), NULL,
222 this);
225 wxeditor_tasinput::xypanel::~xypanel()
227 sws_freeContext(rctx);
230 void wxeditor_tasinput::call_screen_update()
232 for(auto i : inputs)
233 if(i.second.type == portctrl::button::TYPE_LIGHTGUN)
234 i.second.panel->do_redraw();
237 void wxeditor_tasinput::xypanel::on_click(wxMouseEvent& e)
239 CHECK_UI_THREAD;
240 if(!e.Dragging() && !e.LeftDown())
241 return;
242 wxCommandEvent e2(0, wxid);
243 wxSize ps = graphics->GetSize();
244 x = coordinate_to_value(t.xmin, t.xmax, e.GetX(), ps.GetWidth());
245 y = coordinate_to_value(t.ymin, t.ymax, e.GetY(), ps.GetHeight());
246 if(xnum) xnum->SetValue(x);
247 if(ynum) ynum->SetValue(y);
248 do_redraw();
249 (obj->*fun)(e2);
252 void wxeditor_tasinput::xypanel::on_numbers_change(wxSpinEvent& e)
254 CHECK_UI_THREAD;
255 wxCommandEvent e2(0, wxid);
256 if(xnum) x = xnum->GetValue();
257 if(ynum) y = ynum->GetValue();
258 do_redraw();
259 (obj->*fun)(e2);
262 void wxeditor_tasinput::xypanel::do_redraw()
264 CHECK_UI_THREAD;
265 if(!dirty) {
266 dirty = true;
267 graphics->Refresh();
271 void wxeditor_tasinput::xypanel::on_paint(wxPaintEvent& e)
273 CHECK_UI_THREAD;
274 wxPaintDC dc(graphics);
275 if(lightgun) {
276 //Draw the current screen.
277 framebuffer::raw& _fb = inst.fbuf->render_get_latest_screen();
278 framebuffer::fb<false> fb;
279 auto osize = std::make_pair(_fb.get_width(), _fb.get_height());
280 auto size = inst.rom->lightgun_scale();
281 fb.reallocate(osize.first, osize.second, false);
282 fb.copy_from(_fb, 1, 1);
283 inst.fbuf->render_get_latest_screen_end();
284 std::vector<uint8_t> buf;
285 buf.resize(3 * (t.xmax - t.xmin + 1) * (t.ymax - t.ymin + 1));
286 unsigned offX = -t.xmin;
287 unsigned offY = -t.ymin;
288 rctx = sws_getCachedContext(rctx, osize.first, osize.second, PIX_FMT_RGBA,
289 size.first, size.second, PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);
290 uint8_t* srcp[1];
291 int srcs[1];
292 uint8_t* dstp[1];
293 int dsts[1];
294 srcs[0] = 4 * (fb.rowptr(1) - fb.rowptr(0));
295 dsts[0] = 3 * (t.xmax - t.xmin + 1);
296 srcp[0] = reinterpret_cast<unsigned char*>(fb.rowptr(0));
297 dstp[0] = &buf[3 * (offY * (t.xmax - t.xmin + 1) + offX)];
298 memset(&buf[0], 0, buf.size());
299 sws_scale(rctx, srcp, srcs, 0, size.second, dstp, dsts);
300 wxBitmap bmp(wxImage(t.xmax - t.xmin + 1, t.ymax - t.ymin + 1, &buf[0], true));
301 dc.DrawBitmap(bmp, 0, 0, false);
302 } else {
303 dc.SetBackground(*wxWHITE_BRUSH);
304 dc.SetPen(*wxBLACK_PEN);
305 dc.Clear();
307 wxSize ps = graphics->GetSize();
308 dc.DrawLine(0, 0, ps.GetWidth(), 0);
309 dc.DrawLine(0, 0, 0, ps.GetHeight());
310 dc.DrawLine(0, ps.GetHeight() - 1, ps.GetWidth(), ps.GetHeight() - 1);
311 dc.DrawLine(ps.GetWidth() - 1, 0, ps.GetWidth() - 1, ps.GetHeight());
312 int xcenter = (ps.GetWidth() - 1) / 2;
313 int ycenter = (ps.GetHeight() - 1) / 2;
314 if(t.xcenter)
315 dc.DrawLine(xcenter, 0, xcenter, ps.GetHeight());
316 if((t.yindex != std::numeric_limits<unsigned>::max()) && t.ycenter)
317 dc.DrawLine(0, ycenter, ps.GetWidth(), ycenter);
318 dc.SetPen(*wxRED_PEN);
319 int xdraw = value_to_coordinate(t.xmin, t.xmax, x, ps.GetWidth());
320 int ydraw = value_to_coordinate(t.ymin, t.ymax, y, ps.GetHeight());
321 dc.DrawLine(xdraw, 0, xdraw, ps.GetHeight());
322 if((t.yindex != std::numeric_limits<unsigned>::max()) || t.ycenter)
323 dc.DrawLine(0, ydraw, ps.GetWidth(), ydraw);
324 dirty = false;
327 void wxeditor_tasinput::xypanel::Destroy()
329 CHECK_UI_THREAD;
330 graphics->Destroy();
331 xnum->Destroy();
332 if(ynum) ynum->Destroy();
335 wxeditor_tasinput::~wxeditor_tasinput() throw()
337 tasinputs.remove(this->inst);
340 wxeditor_tasinput::wxeditor_tasinput(emulator_instance& _inst, wxWindow* parent)
341 : wxDialog(parent, wxID_ANY, wxT("lsnes: TAS input plugin"), wxDefaultPosition, wxSize(-1, -1)),
342 inst(_inst)
344 CHECK_UI_THREAD;
345 current_controller = 0;
346 current_button = 0;
347 closing = false;
348 Centre();
349 hsizer = new wxBoxSizer(wxHORIZONTAL);
350 SetSizer(hsizer);
351 Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxeditor_tasinput::on_wclose));
352 update_controls();
353 hsizer->SetSizeHints(this);
354 Fit();
356 ahreconfigure.set(inst.dispatch->autohold_reconfigure, [this]() {
357 runuifun([this]() {
358 try {
359 this->update_controls();
360 } catch(std::runtime_error& e) {
361 //Close the window.
362 bool wasc = closing;
363 closing = true;
364 inst.controls->tasinput_enable(false);
365 if(!wasc)
366 Destroy();
372 void wxeditor_tasinput::connect_keyboard_recursive(wxWindow* win)
374 CHECK_UI_THREAD;
375 win->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(wxeditor_tasinput::on_keyboard_down), NULL, this);
376 win->Connect(wxEVT_KEY_UP, wxKeyEventHandler(wxeditor_tasinput::on_keyboard_up), NULL, this);
377 auto i = win->GetChildren().GetFirst();
378 while(i) {
379 connect_keyboard_recursive(i->GetData());
380 i = i->GetNext();
384 void wxeditor_tasinput::on_control(wxCommandEvent& e)
386 CHECK_UI_THREAD;
387 int id = e.GetId();
388 if(!inputs.count(id))
389 return;
390 auto t = inputs[id];
391 int16_t xstate = 0;
392 int16_t ystate = 0;
393 if(t.check)
394 xstate = t.check->GetValue() ? 1 : 0;
395 else if(t.panel) {
396 xstate = t.panel->get_x();
397 ystate = t.panel->get_y();
399 inst.iqueue->run_async([t, xstate, ystate]() {
400 CORE().controls->tasinput(t.port, t.controller, t.xindex, xstate);
401 if(t.yindex != std::numeric_limits<unsigned>::max())
402 CORE().controls->tasinput(t.port, t.controller, t.yindex, ystate);
403 }, [](std::exception& e) {});
406 void wxeditor_tasinput::update_controls()
408 CHECK_UI_THREAD;
409 for(auto i : inputs) {
410 if(i.second.check)
411 i.second.check->Destroy();
412 if(i.second.panel)
413 i.second.panel->Destroy();
415 for(auto i : panels) {
416 hsizer->Detach(i.panel);
417 i.panel->Destroy();
419 inputs.clear();
420 panels.clear();
422 std::vector<control_triple> _inputs;
423 std::vector<std::string> _controller_labels;
424 inst.iqueue->run([&_inputs, &_controller_labels](){
425 std::map<std::string, unsigned> next_in_class;
426 portctrl::frame model = CORE().controls->get_blank();
427 const portctrl::type_set& pts = model.porttypes();
428 unsigned cnum_g = 0;
429 for(unsigned i = 0;; i++) {
430 auto pcid = CORE().controls->lcid_to_pcid(i);
431 if(pcid.first < 0)
432 break;
433 const portctrl::type& pt = pts.port_type(pcid.first);
434 const portctrl::controller_set& pci = *(pt.controller_info);
435 if((ssize_t)pci.controllers.size() <= pcid.second)
436 continue;
437 const portctrl::controller& pc = pci.controllers[pcid.second];
438 //First check that this has non-hidden stuff.
439 bool has_buttons = false;
440 for(unsigned k = 0; k < pc.buttons.size(); k++) {
441 const portctrl::button& pcb = pc.buttons[k];
442 if(!pcb.shadow)
443 has_buttons = true;
445 if(!has_buttons)
446 continue;
447 //Okay, a valid controller.
448 if(!next_in_class.count(pc.cclass))
449 next_in_class[pc.cclass] = 1;
450 uint32_t cnum = next_in_class[pc.cclass]++;
451 _controller_labels.push_back((stringfmt() << pc.cclass << "-" << cnum).str());
452 //Go through all controller pairs.
453 for(unsigned k = 0; k < pc.analog_actions(); k++) {
454 std::pair<unsigned, unsigned> indices = pc.analog_action(k);
455 if(pc.buttons[indices.first].shadow)
456 continue;
457 struct control_triple t;
458 t.port = pcid.first;
459 t.controller = pcid.second;
460 t.xindex = indices.first;
461 t.yindex = indices.second;
462 t.xmin = pc.buttons[t.xindex].rmin;
463 t.xmax = pc.buttons[t.xindex].rmax;
464 t.xcenter = pc.buttons[t.xindex].centers;
465 t.ymin = 0;
466 t.ymax = 1;
467 t.ycenter = false;
468 t.type = pc.buttons[t.xindex].type;
469 t.name = pc.buttons[t.xindex].name;
470 t.logical = cnum_g;
471 if(t.yindex != std::numeric_limits<unsigned>::max()) {
472 t.ymin = pc.buttons[t.yindex].rmin;
473 t.ymax = pc.buttons[t.yindex].rmax;
474 t.ycenter = pc.buttons[t.yindex].centers;
475 t.name = t.name + "/" + pc.buttons[t.yindex].name;
477 _inputs.push_back(t);
479 //Go through all buttons.
480 for(unsigned k = 0; k < pc.buttons.size(); k++) {
481 const portctrl::button& pcb = pc.buttons[k];
482 if(pcb.type != portctrl::button::TYPE_BUTTON || pcb.shadow)
483 continue;
484 struct control_triple t;
485 t.port = pcid.first;
486 t.controller = pcid.second;
487 t.xindex = k;
488 t.yindex = std::numeric_limits<unsigned>::max();
489 t.logical = cnum_g;
490 t.type = portctrl::button::TYPE_BUTTON;
491 t.name = pcb.name;
492 _inputs.push_back(t);
494 for(unsigned k = 0; k < pc.buttons.size(); k++) {
495 const portctrl::button& pcb = pc.buttons[k];
496 if(pcb.type == portctrl::button::TYPE_BUTTON || pcb.shadow)
497 continue;
498 CORE().controls->tasinput(pcid.first, pcid.second, k, pcb.centers ? ((int)pcb.rmin +
499 pcb.rmax) / 2 : pcb.rmin);
501 cnum_g++;
504 int next_id = wxID_HIGHEST + 1;
505 unsigned last_logical = 0xFFFFFFFFUL;
506 wxSizer* current = NULL;
507 wxPanel* current_p = NULL;
508 for(auto i : _inputs) {
509 if(i.logical != last_logical) {
510 //New controller starts.
511 controller_double d;
512 current_p = d.panel = new wxPanel(this, wxID_ANY);
513 d.rtop = new wxBoxSizer(wxVERTICAL);
514 d.panel->SetSizer(d.rtop);
515 d.box = new wxStaticBox(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
516 current = d.top = new wxStaticBoxSizer(d.box, wxVERTICAL);
517 #ifdef __WXMAC__
518 d.label = new wxStaticText(d.panel, wxID_ANY, towxstring(_controller_labels[i.logical]));
519 d.top->Add(d.label);
520 #endif
521 d.rtop->Add(d.top);
522 hsizer->Add(d.panel);
523 panels.push_back(d);
524 last_logical = i.logical;
526 struct control_triple t = i;
527 t.check = NULL;
528 t.panel = NULL;
529 if(i.type == portctrl::button::TYPE_BUTTON) {
530 t.check = new wxCheckBox(current_p, next_id, towxstring(i.name + get_shorthand(i.xindex)),
531 wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
532 current->Add(t.check);
533 t.check->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED,
534 wxCommandEventHandler(wxeditor_tasinput::on_control), NULL, this);
535 } else {
536 t.panel = new xypanel(current_p, inst, current, i, this,
537 wxCommandEventHandler(wxeditor_tasinput::on_control), next_id);
539 inputs[next_id++] = t;
541 if(_inputs.empty()) {
542 throw std::runtime_error("No controlers");
544 auto tx = find_triple(current_controller = 0, current_button = 0);
545 if(tx) {
546 if(tx->check) tx->check->SetFocus();
547 else tx->panel->xnum->SetFocus();
549 //Connect the keyboard.
550 hsizer->Layout();
551 connect_keyboard_recursive(this);
552 Fit();
555 bool wxeditor_tasinput::ShouldPreventAppExit() const { return false; }
557 void wxeditor_tasinput::on_wclose(wxCloseEvent& e)
559 CHECK_UI_THREAD;
560 bool wasc = closing;
561 closing = true;
562 inst.controls->tasinput_enable(false);
563 if(!wasc)
564 Destroy();
567 void wxeditor_tasinput::on_keyboard_down(wxKeyEvent& e)
569 CHECK_UI_THREAD;
570 int key = e.GetKeyCode();
571 if(key == WXK_LEFT || key == WXK_RIGHT || key == WXK_UP || key == WXK_DOWN) {
572 //See if this is associated with a panel.
573 int delta = 1;
574 if(e.GetModifiers() & wxMOD_SHIFT) delta = 999999998;
575 if(e.GetModifiers() & wxMOD_CONTROL) delta = 999999999;
576 if(key == WXK_LEFT || key == WXK_UP) delta = -delta;
577 bool vertical = (key == WXK_UP || key == WXK_DOWN);
578 auto t = find_triple(current_controller, current_button);
579 if(t->panel) {
580 //Handle movement better if span is large.
581 auto ctrl = vertical ? t->panel->ynum : t->panel->xnum;
582 if(!ctrl)
583 return;
584 if(abs(delta) == 999999998) {
585 int& wstep = vertical ? t->panel->ystep : t->panel->xstep;
586 int range = ctrl->GetMax() - ctrl->GetMin();
587 delta = (delta / abs(delta)) * wstep;
588 wstep += sqrt(wstep);
589 if(wstep > range / 20)
590 wstep = range / 20;
592 if(abs(delta) == 999999999) {
593 int range = ctrl->GetMax() - ctrl->GetMin();
594 delta = (delta / abs(delta)) * range / 16;
596 ctrl->SetValue(min(max(ctrl->GetValue() + delta, ctrl->GetMin()), ctrl->GetMax()));
597 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
598 e.SetPosition(ctrl->GetValue());
599 t->panel->on_numbers_change(e);
601 return;
603 if(key == WXK_F5) inst.iqueue->queue("+advance-frame");
606 void wxeditor_tasinput::on_keyboard_up(wxKeyEvent& e)
608 CHECK_UI_THREAD;
609 int key = e.GetKeyCode();
610 if(key == WXK_LEFT || key == WXK_RIGHT) {
611 auto t = find_triple(current_controller, current_button);
612 if(t && t->panel) t->panel->xstep = 1;
614 if(key == WXK_UP || key == WXK_DOWN) {
615 auto t = find_triple(current_controller, current_button);
616 if(t && t->panel) t->panel->ystep = 1;
618 if(key == WXK_TAB) {
619 //Reset speed.
620 auto t = find_triple(current_controller, current_button);
621 if(t && t->panel) t->panel->xstep = t->panel->ystep = 1;
623 if(e.GetModifiers() & wxMOD_SHIFT) {
624 if(current_controller)
625 current_controller--;
626 else
627 current_controller = panels.size() - 1;
628 } else {
629 current_controller++;
630 if(current_controller >= panels.size())
631 current_controller = 0;
633 //Hilight zero control (but don't change). If it is a panel, it is X of it.
634 current_button = 0;
635 t = find_triple(current_controller, 0);
636 if(t) {
637 if(t->check)
638 t->check->SetFocus();
639 else
640 t->panel->xnum->SetFocus();
642 return;
644 for(const char* b = button_codes; *b; b++) {
645 if(key == *b) {
646 //Reset speed.
647 auto t = find_triple(current_controller, current_button);
648 if(t && t->panel) t->panel->xstep = t->panel->ystep = 1;
650 unsigned bn = b - button_codes;
651 //Select (current_controller,bn).
652 t = find_triple(current_controller, bn);
653 if(!t) return;
654 if(t->check) {
655 //Focus and toggle the checkbox.
656 t->check->SetFocus();
657 t->check->SetValue(!t->check->GetValue());
658 //Emit fake event.
659 wxCommandEvent e(wxEVT_COMMAND_CHECKBOX_CLICKED, t->check->GetId());
660 on_control(e);
661 current_button = bn;
662 return;
663 } else if(bn == t->xindex) {
664 //Focus the associated X box.
665 t->panel->xnum->SetFocus();
666 current_button = bn;
667 return;
668 } else if(bn == t->yindex) {
669 //Focus the associated Y box.
670 t->panel->ynum->SetFocus();
671 current_button = bn;
672 return;
674 return;
677 if(key == '\b') {
678 auto t = find_triple(current_controller, current_button);
679 if(t && t->panel) {
680 //Zero this.
681 auto ctrl = t->panel->ynum;
682 if(ctrl)
683 ctrl->SetValue(min(max(0, ctrl->GetMin()), ctrl->GetMax()));
684 ctrl = t->panel->xnum;
685 ctrl->SetValue(min(max(0, ctrl->GetMin()), ctrl->GetMax()));
686 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
687 e.SetPosition(0);
688 t->panel->on_numbers_change(e);
691 if(key == ' ') {
692 //Toggle button.
693 auto t = find_triple(current_controller, current_button);
694 if(t && t->check) {
695 t->check->SetValue(!t->check->GetValue());
696 wxCommandEvent e(wxEVT_COMMAND_CHECKBOX_CLICKED, t->check->GetId());
697 on_control(e);
699 return;
701 if(key == WXK_RETURN) {
702 try {
703 auto t = find_triple(current_controller, current_button);
704 if(!t || !t->panel) return;
705 bool vertical = (current_button == t->yindex);
706 auto ctrl = vertical ? t->panel->ynum : t->panel->xnum;
707 std::string v = pick_text(this, "Enter coordinate", "Enter new coordinate value",
708 (stringfmt() << ctrl->GetValue()).str(), false);
709 int val = parse_value<int>(v);
710 ctrl->SetValue(min(max(val, ctrl->GetMin()), ctrl->GetMax()));
711 wxSpinEvent e(wxEVT_COMMAND_SPINCTRL_UPDATED, ctrl->GetId());
712 e.SetPosition(ctrl->GetValue());
713 t->panel->on_numbers_change(e);
714 } catch(...) {
715 return;
718 if(key == WXK_F1) inst.iqueue->queue("cycle-jukebox-backward");
719 if(key == WXK_F2) inst.iqueue->queue("cycle-jukebox-forward");
720 if(key == WXK_F3) inst.iqueue->queue("save-jukebox");
721 if(key == WXK_F4) inst.iqueue->queue("load-jukebox");
722 if(key == WXK_F5) inst.iqueue->queue("-advance-frame");
725 wxeditor_tasinput::control_triple* wxeditor_tasinput::find_triple(unsigned controller, unsigned control)
727 for(auto& i : inputs) {
728 if(i.second.logical != controller)
729 continue;
730 if(i.second.xindex == control)
731 return &i.second;
732 if(i.second.yindex == control)
733 return &i.second;
735 return NULL;
738 void wxeditor_tasinput_display(wxWindow* parent, emulator_instance& inst)
740 CHECK_UI_THREAD;
741 auto e = tasinputs.lookup(inst);
742 if(e) {
743 e->Raise();
744 return;
746 wxeditor_tasinput* v;
747 try {
748 v = tasinputs.create(inst, parent);
749 } catch(std::runtime_error& e) {
750 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION | wxOK, parent);
751 return;
753 v->Show();
754 inst.controls->tasinput_enable(true);
757 void wxwindow_tasinput_update(emulator_instance& inst)
759 auto e = tasinputs.lookup(inst);
760 if(e) e->call_screen_update();