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"
22 #include <wx/control.h>
23 #include <wx/combobox.h>
24 #include <wx/statline.h>
25 #include <wx/spinctrl.h>
30 #define UINT64_C(val) val##ULL
32 #include <libswscale/swscale.h>
37 const char* button_codes
= "QWERTYUIOPASDFGHJKLZXCVBNM";
39 std::string
get_shorthand(int index
) {
40 if(index
< 0 || index
> (int)strlen(button_codes
))
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)
52 //Scale the values to be zero-based.
53 val
= min(max(val
, rmin
), rmax
);
56 int32_t center
= rmax
/ 2;
57 int32_t cc
= (dim
- 1) / 2;
61 //0 => 0, center => cc.
62 return (val
* (int64_t)cc
+ (center
/ 2)) / center
;
65 //center => cc, rmax => dim - 1.
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)
78 val
= min(max(val
, (int32_t)0), dim
- 1);
79 int32_t center
= (rmax
+ rmin
) / 2;
80 int32_t cc
= (dim
- 1) / 2;
84 //0 => rmin, cc => center.
85 return ((center
- rmin
) * (int64_t)val
+ cc
/ 2) / cc
+ rmin
;
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
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();
108 struct dispatch::target
<> ahreconfigure
;
110 struct control_triple
115 unsigned yindex
; //Used for XY.
116 enum port_controller_button::_type type
;
125 unsigned logical
; //Logical controller. Internal only.
126 std::string name
; //Name. Internal only.
128 struct controller_double
136 struct xypanel
: public wxEvtHandler
138 xypanel(wxWindow
* win
, wxSizer
* s
, control_triple _t
, wxEvtHandler
* _obj
, wxObjectEventFunction _fun
,
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
);
149 friend class wxeditor_tasinput
;
152 wxObjectEventFunction fun
;
160 struct SwsContext
* rctx
;
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
);
169 unsigned current_controller
;
170 unsigned current_button
;
176 wxeditor_tasinput
* tasinput_open
;
179 wxeditor_tasinput::xypanel::xypanel(wxWindow
* win
, wxSizer
* s
, control_triple _t
, wxEvtHandler
* _obj
,
180 wxObjectEventFunction _fun
, int _wxid
)
194 s
->Add(new wxStaticText(win
, wxID_ANY
, towxstring(t
.name
), wxDefaultPosition
, wxDefaultSize
, wxWANTS_CHARS
));
195 s
->Add(graphics
= new wxPanel(win
, wxID_ANY
));
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);
201 graphics
->SetSize(padmajsize
, (t
.yindex
!= std::numeric_limits
<unsigned>::max()) ? padmajsize
:
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
,
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
,
220 wxeditor_tasinput::xypanel::~xypanel()
222 sws_freeContext(rctx
);
225 void wxeditor_tasinput::call_screen_update()
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())
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
);
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();
255 void wxeditor_tasinput::xypanel::do_redraw()
263 void wxeditor_tasinput::xypanel::on_paint(wxPaintEvent
& e
)
265 wxPaintDC
dc(graphics
);
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
);
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);
294 dc
.SetBackground(*wxWHITE_BRUSH
);
295 dc
.SetPen(*wxBLACK_PEN
);
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;
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
);
318 void wxeditor_tasinput::xypanel::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;
334 hsizer
= new wxBoxSizer(wxHORIZONTAL
);
336 Connect(wxEVT_CLOSE_WINDOW
, wxCloseEventHandler(wxeditor_tasinput::on_wclose
));
338 hsizer
->SetSizeHints(this);
341 ahreconfigure
.set(notify_autohold_reconfigure
, [this]() {
344 this->update_controls();
345 } catch(std::runtime_error
& e
) {
349 tasinput_open
= NULL
;
350 lsnes_instance
.controls
.tasinput_enable(false);
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();
364 connect_keyboard_recursive(i
->GetData());
369 void wxeditor_tasinput::on_control(wxCommandEvent
& e
)
372 if(!inputs
.count(id
))
378 xstate
= t
.check
->GetValue() ? 1 : 0;
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
) {
394 i
.second
.check
->Destroy();
396 i
.second
.panel
->Destroy();
398 for(auto i
: panels
) {
399 hsizer
->Detach(i
.panel
);
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();
412 for(unsigned i
= 0;; i
++) {
413 auto pcid
= CORE().controls
.lcid_to_pcid(i
);
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
)
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
];
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
)
440 struct control_triple t
;
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
;
451 t
.type
= pc
.buttons
[t
.xindex
].type
;
452 t
.name
= pc
.buttons
[t
.xindex
].name
;
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
)
467 struct control_triple t
;
469 t
.controller
= pcid
.second
;
471 t
.yindex
= std::numeric_limits
<unsigned>::max();
473 t
.type
= port_controller_button::TYPE_BUTTON
;
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
)
481 CORE().controls
.tasinput(pcid
.first
, pcid
.second
, k
, pcb
.centers
? ((int)pcb
.rmin
+
482 pcb
.rmax
) / 2 : pcb
.rmin
);
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.
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
);
501 d
.label
= new wxStaticText(d
.panel
, wxID_ANY
, towxstring(_controller_labels
[i
.logical
]));
505 hsizer
->Add(d
.panel
);
507 last_logical
= i
.logical
;
509 struct control_triple t
= i
;
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);
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);
529 if(tx
->check
) tx
->check
->SetFocus();
530 else tx
->panel
->xnum
->SetFocus();
532 //Connect the keyboard.
534 connect_keyboard_recursive(this);
538 bool wxeditor_tasinput::ShouldPreventAppExit() const { return false; }
540 void wxeditor_tasinput::on_wclose(wxCloseEvent
& e
)
544 tasinput_open
= NULL
;
545 lsnes_instance
.controls
.tasinput_enable(false);
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.
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
);
562 //Handle movement better if span is large.
563 auto ctrl
= vertical
? t
->panel
->ynum
: t
->panel
->xnum
;
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)
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
);
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;
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
--;
608 current_controller
= panels
.size() - 1;
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.
616 t
= find_triple(current_controller
, 0);
619 t
->check
->SetFocus();
621 t
->panel
->xnum
->SetFocus();
625 for(const char* b
= button_codes
; *b
; b
++) {
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
);
636 //Focus and toggle the checkbox.
637 t
->check
->SetFocus();
638 t
->check
->SetValue(!t
->check
->GetValue());
640 wxCommandEvent
e(wxEVT_COMMAND_CHECKBOX_CLICKED
, t
->check
->GetId());
644 } else if(bn
== t
->xindex
) {
645 //Focus the associated X box.
646 t
->panel
->xnum
->SetFocus();
649 } else if(bn
== t
->yindex
) {
650 //Focus the associated Y box.
651 t
->panel
->ynum
->SetFocus();
659 auto t
= find_triple(current_controller
, current_button
);
662 auto ctrl
= t
->panel
->ynum
;
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());
669 t
->panel
->on_numbers_change(e
);
674 auto t
= find_triple(current_controller
, current_button
);
676 t
->check
->SetValue(!t
->check
->GetValue());
677 wxCommandEvent
e(wxEVT_COMMAND_CHECKBOX_CLICKED
, t
->check
->GetId());
682 if(key
== WXK_RETURN
) {
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
);
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
)
711 if(i
.second
.xindex
== control
)
713 if(i
.second
.yindex
== control
)
719 void wxeditor_tasinput_display(wxWindow
* parent
)
723 wxeditor_tasinput
* v
;
725 v
= new wxeditor_tasinput(parent
);
726 } catch(std::runtime_error
& e
) {
727 wxMessageBox(_T("No controllers present"), _T("Error"), wxICON_EXCLAMATION
| wxOK
, parent
);
732 lsnes_instance
.controls
.tasinput_enable(true);
735 void wxwindow_tasinput_update()
738 tasinput_open
->call_screen_update();