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"
31 #define UINT64_C(val) val##ULL
33 #include <libswscale/swscale.h>
38 const char* button_codes
= "QWERTYUIOPASDFGHJKLZXCVBNM";
40 std::string
get_shorthand(int index
) {
41 if(index
< 0 || index
> (int)strlen(button_codes
))
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)
53 //Scale the values to be zero-based.
54 val
= min(max(val
, rmin
), rmax
);
57 int32_t center
= rmax
/ 2;
58 int32_t cc
= (dim
- 1) / 2;
62 //0 => 0, center => cc.
63 return (val
* (int64_t)cc
+ (center
/ 2)) / center
;
66 //center => cc, rmax => dim - 1.
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)
79 val
= min(max(val
, (int32_t)0), dim
- 1);
80 int32_t center
= (rmax
+ rmin
) / 2;
81 int32_t cc
= (dim
- 1) / 2;
85 //0 => rmin, cc => center.
86 return ((center
- rmin
) * (int64_t)val
+ cc
/ 2) / cc
+ rmin
;
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
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();
109 struct dispatch::target
<> ahreconfigure
;
111 struct control_triple
116 unsigned yindex
; //Used for XY.
117 enum portctrl::button::_type type
;
126 unsigned logical
; //Logical controller. Internal only.
127 std::string name
; //Name. Internal only.
129 struct controller_double
137 struct xypanel
: public wxEvtHandler
139 xypanel(wxWindow
* win
, emulator_instance
& _inst
, wxSizer
* s
, control_triple _t
, wxEvtHandler
* _obj
,
140 wxObjectEventFunction _fun
, int _wxid
);
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
);
150 friend class wxeditor_tasinput
;
151 emulator_instance
& inst
;
154 wxObjectEventFunction fun
;
162 struct SwsContext
* rctx
;
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
);
172 unsigned current_controller
;
173 unsigned current_button
;
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
)
199 s
->Add(new wxStaticText(win
, wxID_ANY
, towxstring(t
.name
), wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
));
200 s
->Add(graphics
= new wxPanel(win
, wxID_ANY
));
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);
206 graphics
->SetSize(padmajsize
, (t
.yindex
!= std::numeric_limits
<unsigned>::max()) ? padmajsize
:
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
,
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
,
225 wxeditor_tasinput::xypanel::~xypanel()
227 sws_freeContext(rctx
);
230 void wxeditor_tasinput::call_screen_update()
233 if(i
.second
.type
== portctrl::button::TYPE_LIGHTGUN
)
234 i
.second
.panel
->do_redraw();
237 void wxeditor_tasinput::xypanel::on_click(wxMouseEvent
& e
)
240 if(!e
.Dragging() && !e
.LeftDown())
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
);
252 void wxeditor_tasinput::xypanel::on_numbers_change(wxSpinEvent
& e
)
255 wxCommandEvent
e2(0, wxid
);
256 if(xnum
) x
= xnum
->GetValue();
257 if(ynum
) y
= ynum
->GetValue();
262 void wxeditor_tasinput::xypanel::do_redraw()
271 void wxeditor_tasinput::xypanel::on_paint(wxPaintEvent
& e
)
274 wxPaintDC
dc(graphics
);
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
, AV_PIX_FMT_RGBA
,
289 size
.first
, size
.second
, AV_PIX_FMT_BGR24
, SWS_POINT
, NULL
, NULL
, NULL
);
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);
303 dc
.SetBackground(*wxWHITE_BRUSH
);
304 dc
.SetPen(*wxBLACK_PEN
);
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;
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
);
327 void wxeditor_tasinput::xypanel::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)),
345 current_controller
= 0;
349 hsizer
= new wxBoxSizer(wxHORIZONTAL
);
351 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_tasinput::on_wclose
));
353 hsizer
->SetSizeHints(this);
356 ahreconfigure
.set(inst
.dispatch
->autohold_reconfigure
, [this]() {
359 this->update_controls();
360 } catch(std::runtime_error
& e
) {
364 inst
.controls
->tasinput_enable(false);
372 void wxeditor_tasinput::connect_keyboard_recursive(wxWindow
* win
)
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();
379 connect_keyboard_recursive(i
->GetData());
384 void wxeditor_tasinput::on_control(wxCommandEvent
& e
)
388 if(!inputs
.count(id
))
394 xstate
= t
.check
->GetValue() ? 1 : 0;
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()
409 for(auto i
: inputs
) {
411 i
.second
.check
->Destroy();
413 i
.second
.panel
->Destroy();
415 for(auto i
: panels
) {
416 hsizer
->Detach(i
.panel
);
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();
429 for(unsigned i
= 0;; i
++) {
430 auto pcid
= CORE().controls
->lcid_to_pcid(i
);
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
)
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
];
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
)
457 struct control_triple t
;
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
;
468 t
.type
= pc
.buttons
[t
.xindex
].type
;
469 t
.name
= pc
.buttons
[t
.xindex
].name
;
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
)
484 struct control_triple t
;
486 t
.controller
= pcid
.second
;
488 t
.yindex
= std::numeric_limits
<unsigned>::max();
490 t
.type
= portctrl::button::TYPE_BUTTON
;
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
)
498 CORE().controls
->tasinput(pcid
.first
, pcid
.second
, k
, pcb
.centers
? ((int)pcb
.rmin
+
499 pcb
.rmax
) / 2 : pcb
.rmin
);
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.
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
);
518 hsizer
->Add(d
.panel
);
520 last_logical
= i
.logical
;
522 struct control_triple t
= i
;
525 if(i
.type
== portctrl::button::TYPE_BUTTON
) {
526 t
.check
= new wxCheckBox(current_p
, next_id
, towxstring(i
.name
+ get_shorthand(i
.xindex
)),
527 wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
);
528 current
->Add(t
.check
);
529 t
.check
->Connect(wxEVT_COMMAND_CHECKBOX_CLICKED
,
530 wxCommandEventHandler(wxeditor_tasinput::on_control
), NULL
, this);
532 t
.panel
= new xypanel(current_p
, inst
, current
, i
, this,
533 wxCommandEventHandler(wxeditor_tasinput::on_control
), next_id
);
535 inputs
[next_id
++] = t
;
537 if(_inputs
.empty()) {
538 throw std::runtime_error("No controlers");
540 auto tx
= find_triple(current_controller
= 0, current_button
= 0);
542 if(tx
->check
) tx
->check
->SetFocus();
543 else tx
->panel
->xnum
->SetFocus();
545 //Connect the keyboard.
547 connect_keyboard_recursive(this);
551 bool wxeditor_tasinput::ShouldPreventAppExit() const { return false; }
553 void wxeditor_tasinput::on_wclose(wxCloseEvent
& e
)
558 inst
.controls
->tasinput_enable(false);
563 void wxeditor_tasinput::on_keyboard_down(wxKeyEvent
& e
)
566 int key
= e
.GetKeyCode();
567 if(key
== WXK_LEFT
|| key
== WXK_RIGHT
|| key
== WXK_UP
|| key
== WXK_DOWN
) {
568 //See if this is associated with a panel.
570 if(e
.GetModifiers() & wxMOD_SHIFT
) delta
= 999999998;
571 if(e
.GetModifiers() & wxMOD_CONTROL
) delta
= 999999999;
572 if(key
== WXK_LEFT
|| key
== WXK_UP
) delta
= -delta
;
573 bool vertical
= (key
== WXK_UP
|| key
== WXK_DOWN
);
574 auto t
= find_triple(current_controller
, current_button
);
576 //Handle movement better if span is large.
577 auto ctrl
= vertical
? t
->panel
->ynum
: t
->panel
->xnum
;
580 if(abs(delta
) == 999999998) {
581 int& wstep
= vertical
? t
->panel
->ystep
: t
->panel
->xstep
;
582 int range
= ctrl
->GetMax() - ctrl
->GetMin();
583 delta
= (delta
/ abs(delta
)) * wstep
;
584 wstep
+= sqrt(wstep
);
585 if(wstep
> range
/ 20)
588 if(abs(delta
) == 999999999) {
589 int range
= ctrl
->GetMax() - ctrl
->GetMin();
590 delta
= (delta
/ abs(delta
)) * range
/ 16;
592 ctrl
->SetValue(min(max(ctrl
->GetValue() + delta
, ctrl
->GetMin()), ctrl
->GetMax()));
593 wxSpinEvent
e(wxEVT_COMMAND_SPINCTRL_UPDATED
, ctrl
->GetId());
594 e
.SetPosition(ctrl
->GetValue());
595 t
->panel
->on_numbers_change(e
);
599 if(key
== WXK_F5
) inst
.iqueue
->queue("+advance-frame");
602 void wxeditor_tasinput::on_keyboard_up(wxKeyEvent
& e
)
605 int key
= e
.GetKeyCode();
606 if(key
== WXK_LEFT
|| key
== WXK_RIGHT
) {
607 auto t
= find_triple(current_controller
, current_button
);
608 if(t
&& t
->panel
) t
->panel
->xstep
= 1;
610 if(key
== WXK_UP
|| key
== WXK_DOWN
) {
611 auto t
= find_triple(current_controller
, current_button
);
612 if(t
&& t
->panel
) t
->panel
->ystep
= 1;
616 auto t
= find_triple(current_controller
, current_button
);
617 if(t
&& t
->panel
) t
->panel
->xstep
= t
->panel
->ystep
= 1;
619 if(e
.GetModifiers() & wxMOD_SHIFT
) {
620 if(current_controller
)
621 current_controller
--;
623 current_controller
= panels
.size() - 1;
625 current_controller
++;
626 if(current_controller
>= panels
.size())
627 current_controller
= 0;
629 //Hilight zero control (but don't change). If it is a panel, it is X of it.
631 t
= find_triple(current_controller
, 0);
634 t
->check
->SetFocus();
636 t
->panel
->xnum
->SetFocus();
640 for(const char* b
= button_codes
; *b
; b
++) {
643 auto t
= find_triple(current_controller
, current_button
);
644 if(t
&& t
->panel
) t
->panel
->xstep
= t
->panel
->ystep
= 1;
646 unsigned bn
= b
- button_codes
;
647 //Select (current_controller,bn).
648 t
= find_triple(current_controller
, bn
);
651 //Focus and toggle the checkbox.
652 t
->check
->SetFocus();
653 t
->check
->SetValue(!t
->check
->GetValue());
655 wxCommandEvent
e(wxEVT_COMMAND_CHECKBOX_CLICKED
, t
->check
->GetId());
659 } else if(bn
== t
->xindex
) {
660 //Focus the associated X box.
661 t
->panel
->xnum
->SetFocus();
664 } else if(bn
== t
->yindex
) {
665 //Focus the associated Y box.
666 t
->panel
->ynum
->SetFocus();
674 auto t
= find_triple(current_controller
, current_button
);
677 auto ctrl
= t
->panel
->ynum
;
679 ctrl
->SetValue(min(max(0, ctrl
->GetMin()), ctrl
->GetMax()));
680 ctrl
= t
->panel
->xnum
;
681 ctrl
->SetValue(min(max(0, ctrl
->GetMin()), ctrl
->GetMax()));
682 wxSpinEvent
e(wxEVT_COMMAND_SPINCTRL_UPDATED
, ctrl
->GetId());
684 t
->panel
->on_numbers_change(e
);
689 auto t
= find_triple(current_controller
, current_button
);
691 t
->check
->SetValue(!t
->check
->GetValue());
692 wxCommandEvent
e(wxEVT_COMMAND_CHECKBOX_CLICKED
, t
->check
->GetId());
697 if(key
== WXK_RETURN
) {
699 auto t
= find_triple(current_controller
, current_button
);
700 if(!t
|| !t
->panel
) return;
701 bool vertical
= (current_button
== t
->yindex
);
702 auto ctrl
= vertical
? t
->panel
->ynum
: t
->panel
->xnum
;
703 std::string v
= pick_text(this, "Enter coordinate", "Enter new coordinate value",
704 (stringfmt() << ctrl
->GetValue()).str(), false);
705 int val
= parse_value
<int>(v
);
706 ctrl
->SetValue(min(max(val
, ctrl
->GetMin()), ctrl
->GetMax()));
707 wxSpinEvent
e(wxEVT_COMMAND_SPINCTRL_UPDATED
, ctrl
->GetId());
708 e
.SetPosition(ctrl
->GetValue());
709 t
->panel
->on_numbers_change(e
);
714 if(key
== WXK_F1
) inst
.iqueue
->queue("cycle-jukebox-backward");
715 if(key
== WXK_F2
) inst
.iqueue
->queue("cycle-jukebox-forward");
716 if(key
== WXK_F3
) inst
.iqueue
->queue("save-jukebox");
717 if(key
== WXK_F4
) inst
.iqueue
->queue("load-jukebox");
718 if(key
== WXK_F5
) inst
.iqueue
->queue("-advance-frame");
721 wxeditor_tasinput::control_triple
* wxeditor_tasinput::find_triple(unsigned controller
, unsigned control
)
723 for(auto& i
: inputs
) {
724 if(i
.second
.logical
!= controller
)
726 if(i
.second
.xindex
== control
)
728 if(i
.second
.yindex
== control
)
734 void wxeditor_tasinput_display(wxWindow
* parent
, emulator_instance
& inst
)
737 auto e
= tasinputs
.lookup(inst
);
742 wxeditor_tasinput
* v
;
744 v
= tasinputs
.create(inst
, parent
);
745 } catch(std::runtime_error
& e
) {
746 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, parent
);
750 inst
.controls
->tasinput_enable(true);
753 void wxwindow_tasinput_update(emulator_instance
& inst
)
755 auto e
= tasinputs
.lookup(inst
);
756 if(e
) e
->call_screen_update();