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"
23 #include <wx/control.h>
24 #include <wx/combobox.h>
25 #include <wx/statline.h>
26 #include <wx/spinctrl.h>
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 port_controller_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
)
198 s
->Add(new wxStaticText(win
, wxID_ANY
, towxstring(t
.name
), wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
));
199 s
->Add(graphics
= new wxPanel(win
, wxID_ANY
));
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);
205 graphics
->SetSize(padmajsize
, (t
.yindex
!= std::numeric_limits
<unsigned>::max()) ? padmajsize
:
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
,
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
,
224 wxeditor_tasinput::xypanel::~xypanel()
226 sws_freeContext(rctx
);
229 void wxeditor_tasinput::call_screen_update()
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())
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
);
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();
259 void wxeditor_tasinput::xypanel::do_redraw()
267 void wxeditor_tasinput::xypanel::on_paint(wxPaintEvent
& e
)
269 wxPaintDC
dc(graphics
);
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
);
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);
298 dc
.SetBackground(*wxWHITE_BRUSH
);
299 dc
.SetPen(*wxBLACK_PEN
);
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;
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
);
322 void wxeditor_tasinput::xypanel::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)),
338 current_controller
= 0;
342 hsizer
= new wxBoxSizer(wxHORIZONTAL
);
344 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_tasinput::on_wclose
));
346 hsizer
->SetSizeHints(this);
349 ahreconfigure
.set(inst
.dispatch
->autohold_reconfigure
, [this]() {
352 this->update_controls();
353 } catch(std::runtime_error
& e
) {
357 inst
.controls
->tasinput_enable(false);
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();
371 connect_keyboard_recursive(i
->GetData());
376 void wxeditor_tasinput::on_control(wxCommandEvent
& e
)
379 if(!inputs
.count(id
))
385 xstate
= t
.check
->GetValue() ? 1 : 0;
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
) {
401 i
.second
.check
->Destroy();
403 i
.second
.panel
->Destroy();
405 for(auto i
: panels
) {
406 hsizer
->Detach(i
.panel
);
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();
419 for(unsigned i
= 0;; i
++) {
420 auto pcid
= CORE().controls
->lcid_to_pcid(i
);
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
)
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
];
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
)
447 struct control_triple t
;
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
;
458 t
.type
= pc
.buttons
[t
.xindex
].type
;
459 t
.name
= pc
.buttons
[t
.xindex
].name
;
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
)
474 struct control_triple t
;
476 t
.controller
= pcid
.second
;
478 t
.yindex
= std::numeric_limits
<unsigned>::max();
480 t
.type
= port_controller_button::TYPE_BUTTON
;
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
)
488 CORE().controls
->tasinput(pcid
.first
, pcid
.second
, k
, pcb
.centers
? ((int)pcb
.rmin
+
489 pcb
.rmax
) / 2 : pcb
.rmin
);
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.
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
);
508 d
.label
= new wxStaticText(d
.panel
, wxID_ANY
, towxstring(_controller_labels
[i
.logical
]));
512 hsizer
->Add(d
.panel
);
514 last_logical
= i
.logical
;
516 struct control_triple t
= i
;
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);
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);
536 if(tx
->check
) tx
->check
->SetFocus();
537 else tx
->panel
->xnum
->SetFocus();
539 //Connect the keyboard.
541 connect_keyboard_recursive(this);
545 bool wxeditor_tasinput::ShouldPreventAppExit() const { return false; }
547 void wxeditor_tasinput::on_wclose(wxCloseEvent
& e
)
551 inst
.controls
->tasinput_enable(false);
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.
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
);
568 //Handle movement better if span is large.
569 auto ctrl
= vertical
? t
->panel
->ynum
: t
->panel
->xnum
;
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)
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
);
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;
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
--;
614 current_controller
= panels
.size() - 1;
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.
622 t
= find_triple(current_controller
, 0);
625 t
->check
->SetFocus();
627 t
->panel
->xnum
->SetFocus();
631 for(const char* b
= button_codes
; *b
; b
++) {
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
);
642 //Focus and toggle the checkbox.
643 t
->check
->SetFocus();
644 t
->check
->SetValue(!t
->check
->GetValue());
646 wxCommandEvent
e(wxEVT_COMMAND_CHECKBOX_CLICKED
, t
->check
->GetId());
650 } else if(bn
== t
->xindex
) {
651 //Focus the associated X box.
652 t
->panel
->xnum
->SetFocus();
655 } else if(bn
== t
->yindex
) {
656 //Focus the associated Y box.
657 t
->panel
->ynum
->SetFocus();
665 auto t
= find_triple(current_controller
, current_button
);
668 auto ctrl
= t
->panel
->ynum
;
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());
675 t
->panel
->on_numbers_change(e
);
680 auto t
= find_triple(current_controller
, current_button
);
682 t
->check
->SetValue(!t
->check
->GetValue());
683 wxCommandEvent
e(wxEVT_COMMAND_CHECKBOX_CLICKED
, t
->check
->GetId());
688 if(key
== WXK_RETURN
) {
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
);
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
)
717 if(i
.second
.xindex
== control
)
719 if(i
.second
.yindex
== control
)
725 void wxeditor_tasinput_display(wxWindow
* parent
, emulator_instance
& inst
)
727 auto e
= tasinputs
.lookup(inst
);
732 wxeditor_tasinput
* v
;
734 v
= tasinputs
.create(inst
, parent
);
735 } catch(std::runtime_error
& e
) {
736 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, parent
);
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();