1 #include "controller.hpp"
3 #include "mainloop.hpp"
8 #include "framebuffer.hpp"
9 #include <snes/snes.hpp>
10 #include <ui-libsnes/libsnes.hpp>
12 #define BUTTON_LEFT 0 //Gamepad
13 #define BUTTON_RIGHT 1 //Gamepad
14 #define BUTTON_UP 2 //Gamepad
15 #define BUTTON_DOWN 3 //Gamepad
16 #define BUTTON_A 4 //Gamepad
17 #define BUTTON_B 5 //Gamepad
18 #define BUTTON_X 6 //Gamepad
19 #define BUTTON_Y 7 //Gamepad
20 #define BUTTON_L 8 //Gamepad & Mouse
21 #define BUTTON_R 9 //Gamepad & Mouse
22 #define BUTTON_SELECT 10 //Gamepad
23 #define BUTTON_START 11 //Gamepad & Justifier
24 #define BUTTON_TRIGGER 12 //Superscope.
25 #define BUTTON_CURSOR 13 //Superscope & Justifier
26 #define BUTTON_PAUSE 14 //Superscope
27 #define BUTTON_TURBO 15 //Superscope
31 porttype_t porttypes
[2];
32 int analog_indices
[3] = {-1, -1, -1};
33 bool analog_is_mouse
[3];
35 controls_t curcontrols
;
36 controls_t autoheld_controls
;
37 std::vector
<controls_t
> autofire_pattern
;
39 void update_analog_indices() throw()
42 for(unsigned j
= 0; j
< sizeof(analog_indices
) / sizeof(analog_indices
[0]); j
++)
43 analog_indices
[j
] = -1;
44 for(unsigned j
= 0; j
< 8; j
++) {
45 devicetype_t d
= controller_type_by_logical(j
);
51 analog_is_mouse
[i
] = true;
52 analog_indices
[i
++] = j
;
56 analog_is_mouse
[i
] = false;
57 analog_indices
[i
++] = j
;
63 std::map
<std::string
, std::pair
<unsigned, unsigned>> buttonmap
;
65 const char* buttonnames
[] = {
66 "left", "right", "up", "down", "A", "B", "X", "Y", "L", "R", "select", "start", "trigger", "cursor",
75 for(unsigned i
= 0; i
< 8; i
++)
76 for(unsigned j
= 0; j
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); j
++) {
78 x
<< (i
+ 1) << buttonnames
[j
];
79 buttonmap
[x
.str()] = std::make_pair(i
, j
);
85 void do_button_action(unsigned ui_id
, unsigned button
, short newstate
, bool do_xor
, controls_t
& c
)
87 enum devicetype_t p
= controller_type_by_logical(ui_id
);
88 int x
= controller_index_by_logical(ui_id
);
92 window::out() << "No such controller #" << (ui_id
+ 1) << std::endl
;
96 case BUTTON_UP
: bid
= SNES_DEVICE_ID_JOYPAD_UP
; break;
97 case BUTTON_DOWN
: bid
= SNES_DEVICE_ID_JOYPAD_DOWN
; break;
98 case BUTTON_LEFT
: bid
= SNES_DEVICE_ID_JOYPAD_LEFT
; break;
99 case BUTTON_RIGHT
: bid
= SNES_DEVICE_ID_JOYPAD_RIGHT
; break;
100 case BUTTON_A
: bid
= SNES_DEVICE_ID_JOYPAD_A
; break;
101 case BUTTON_B
: bid
= SNES_DEVICE_ID_JOYPAD_B
; break;
102 case BUTTON_X
: bid
= SNES_DEVICE_ID_JOYPAD_X
; break;
103 case BUTTON_Y
: bid
= SNES_DEVICE_ID_JOYPAD_Y
; break;
104 case BUTTON_L
: bid
= SNES_DEVICE_ID_JOYPAD_L
; break;
105 case BUTTON_R
: bid
= SNES_DEVICE_ID_JOYPAD_R
; break;
106 case BUTTON_SELECT
: bid
= SNES_DEVICE_ID_JOYPAD_SELECT
; break;
107 case BUTTON_START
: bid
= SNES_DEVICE_ID_JOYPAD_START
; break;
109 window::out() << "Invalid button for gamepad" << std::endl
;
115 case BUTTON_L
: bid
= SNES_DEVICE_ID_MOUSE_LEFT
; break;
116 case BUTTON_R
: bid
= SNES_DEVICE_ID_MOUSE_RIGHT
; break;
118 window::out() << "Invalid button for mouse" << std::endl
;
124 case BUTTON_START
: bid
= SNES_DEVICE_ID_JUSTIFIER_START
; break;
125 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_JUSTIFIER_TRIGGER
; break;
127 window::out() << "Invalid button for justifier" << std::endl
;
133 case BUTTON_TRIGGER
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER
; break;
134 case BUTTON_CURSOR
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_CURSOR
; break;
135 case BUTTON_PAUSE
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_PAUSE
; break;
136 case BUTTON_TURBO
: bid
= SNES_DEVICE_ID_SUPER_SCOPE_TURBO
; break;
138 window::out() << "Invalid button for superscope" << std::endl
;
144 c((x
& 4) ? 1 : 0, x
& 3, bid
) ^= newstate
;
146 c((x
& 4) ? 1 : 0, x
& 3, bid
) = newstate
;
150 void do_button_action(unsigned ui_id
, unsigned button
, short newstate
, bool do_xor
= false)
153 do_button_action(ui_id
, button
, newstate
, do_xor
, autoheld_controls
);
155 do_button_action(ui_id
, button
, newstate
, do_xor
, curcontrols
);
158 function_ptr_command
<tokensplitter
&> autofire("autofire", "Set autofire pattern",
159 "Syntax: autofire <buttons|->...\nSet autofire pattern\n",
160 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
162 throw std::runtime_error("Need at least one frame for autofire");
163 std::vector
<controls_t
> new_autofire_pattern
;
166 std::string fpattern
= t
;
168 new_autofire_pattern
.push_back(controls_t());
171 while(fpattern
!= "") {
172 size_t split
= fpattern
.find_first_of(",");
173 std::string button
= fpattern
;
175 if(split
< fpattern
.length()) {
176 button
= fpattern
.substr(0, split
);
177 rest
= fpattern
.substr(split
+ 1);
179 if(!buttonmap
.count(button
)) {
180 std::ostringstream x
;
181 x
<< "Invalid button '" << button
<< "'";
182 throw std::runtime_error(x
.str());
184 auto g
= buttonmap
[button
];
185 do_button_action(g
.first
, g
.second
, 1, false, c
);
188 new_autofire_pattern
.push_back(c
);
191 autofire_pattern
= new_autofire_pattern
;
194 class button_action
: public command
197 button_action(const std::string
& cmd
, int _type
, unsigned _controller
, std::string _button
)
198 throw(std::bad_alloc
)
203 controller
= _controller
;
206 ~button_action() throw() {}
207 void invoke(const std::string
& args
) throw(std::bad_alloc
, std::runtime_error
)
210 throw std::runtime_error("This command does not take parameters");
212 if(!buttonmap
.count(button
))
214 auto i
= buttonmap
[button
];
215 do_button_action(i
.first
, i
.second
, (type
!= 1) ? 1 : 0, (type
== 2));
216 update_movie_state();
217 window::notify_screen_update();
219 std::string
get_short_help() throw(std::bad_alloc
)
221 return "Press/Unpress button";
223 std::string
get_long_help() throw(std::bad_alloc
)
225 return "Syntax: " + commandn
+ "\n"
226 "Presses/Unpresses button\n";
228 std::string commandn
;
234 class button_action_helper
237 button_action_helper()
239 for(size_t i
= 0; i
< sizeof(buttonnames
) / sizeof(buttonnames
[0]); ++i
)
240 for(int j
= 0; j
< 3; ++j
)
241 for(unsigned k
= 0; k
< 8; ++k
) {
242 std::ostringstream x
, y
;
258 new button_action(x
.str(), j
, k
, y
.str());
265 int controller_index_by_logical(unsigned lid
) throw()
267 bool p1multitap
= (porttypes
[0] == PT_MULTITAP
);
268 unsigned p1devs
= port_types
[porttypes
[0]].devices
;
269 unsigned p2devs
= port_types
[porttypes
[1]].devices
;
270 if(lid
>= p1devs
+ p2devs
)
276 return 4 + lid
- p1devs
;
286 int controller_index_by_analog(unsigned aid
) throw()
290 return analog_indices
[aid
];
293 bool controller_ismouse_by_analog(unsigned aid
) throw()
297 return analog_is_mouse
[aid
];
300 devicetype_t
controller_type_by_logical(unsigned lid
) throw()
302 int x
= controller_index_by_logical(lid
);
305 enum porttype_t rawtype
= porttypes
[x
>> 2];
306 if((x
& 3) < port_types
[rawtype
].devices
)
307 return port_types
[rawtype
].dtype
;
312 void controller_set_port_type(unsigned port
, porttype_t ptype
, bool set_core
) throw()
314 if(set_core
&& ptype
!= PT_INVALID
)
315 snes_set_controller_port_device(port
!= 0, port_types
[ptype
].bsnes_type
);
316 porttypes
[port
] = ptype
;
317 update_analog_indices();
320 controls_t
get_current_controls(uint64_t frame
)
322 if(autofire_pattern
.size())
323 return curcontrols
^ autoheld_controls
^ autofire_pattern
[frame
% autofire_pattern
.size()];
325 return curcontrols
^ autoheld_controls
;
328 void send_analog_input(int32_t x
, int32_t y
, unsigned index
)
330 if(controller_ismouse_by_analog(index
)) {
331 x
-= (framebuffer
.width
/ 2);
332 y
-= (framebuffer
.height
/ 2);
334 auto g
= get_scale_factors(framebuffer
.width
, framebuffer
.height
);
338 int aindex
= controller_index_by_analog(index
);
340 window::out() << "No analog controller in slot #" << (index
+ 1) << std::endl
;
343 curcontrols(aindex
>> 2, aindex
& 3, 0) = x
;
344 curcontrols(aindex
>> 2, aindex
& 3, 1) = y
;
347 void set_curcontrols_reset(int32_t delay
)
350 curcontrols(CONTROL_SYSTEM_RESET
) = 1;
351 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = delay
/ 10000;
352 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = delay
% 10000;
354 curcontrols(CONTROL_SYSTEM_RESET
) = 0;
355 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_HI
) = 0;
356 curcontrols(CONTROL_SYSTEM_RESET_CYCLES_LO
) = 0;