Refactor internal interfaces to support arbitrary number of controllers

This commit is contained in:
Ilari Liusvaara 2012-04-15 15:10:15 +03:00
parent 4f9dfd513c
commit 07ecfd7dc4
16 changed files with 396 additions and 309 deletions

View file

@ -37,18 +37,6 @@
* Maximum number of ports. * Maximum number of ports.
*/ */
#define MAX_PORTS 2 #define MAX_PORTS 2
/**
* Maximum number of controllers per one port.
*/
#define MAX_CONTROLLERS_PER_PORT 4
/**
* Maximum numbers of controls per one controller.
*/
#define MAX_CONTROLS_PER_CONTROLLER 12
/**
* Number of button controls.
*/
#define MAX_BUTTONS MAX_PORTS * MAX_CONTROLLERS_PER_PORT * MAX_CONTROLS_PER_CONTROLLER
/** /**
* Size of controller page. * Size of controller page.
*/ */
@ -403,28 +391,20 @@ public:
/** /**
* Clear specified DRDY bit. * Clear specified DRDY bit.
* *
* Parameter port: The port ID.
* Parameter pcid: The physical controller id. * Parameter pcid: The physical controller id.
* Parameter ctrl: The control id. * Parameter ctrl: The control id.
*/ */
void clear_DRDY(unsigned pcid, unsigned ctrl) throw(); void clear_DRDY(unsigned port, unsigned pcid, unsigned ctrl) throw();
/** /**
* Get state of DRDY bit. * Get state of DRDY bit.
* *
* Parameter port: The port ID.
* Parameter pcid: The physical controller id. * Parameter pcid: The physical controller id.
* Parameter ctrl: The control id. * Parameter ctrl: The control id.
* Returns: The DRDY state. * Returns: The DRDY state.
*/ */
bool get_DRDY(unsigned pcid, unsigned ctrl) throw(); bool get_DRDY(unsigned port, unsigned pcid, unsigned ctrl) throw();
/**
* Get state of DRDY bit.
*
* Parameter idx: The control index.
* Returns: The DRDY state.
*/
bool get_DRDY(unsigned idx) throw()
{
return get_DRDY(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
}
/** /**
* Is any poll count nonzero or is system flag set? * Is any poll count nonzero or is system flag set?
* *
@ -434,29 +414,34 @@ public:
/** /**
* Read the actual poll count on specified control. * Read the actual poll count on specified control.
* *
* Parameter port: The port ID.
* Parameter pcid: The physical controller id. * Parameter pcid: The physical controller id.
* Parameter ctrl: The control id. * Parameter ctrl: The control id.
* Return: The poll count. * Return: The poll count.
*/ */
uint32_t get_polls(unsigned pcid, unsigned ctrl) throw(); uint32_t get_polls(unsigned port, unsigned pcid, unsigned ctrl) throw();
/**
* Get number of controls.
*
* Returns: The control count.
*/
unsigned maxbuttons() throw();
/** /**
* Read the actual poll count on specified control. * Read the actual poll count on specified control.
* *
* Parameter idx: The control index. * Parameter idx: The control index.
* Return: The poll count. * Return: The poll count.
*/ */
uint32_t get_polls(unsigned idx) throw() uint32_t get_polls(unsigned idx) throw();
{
return get_polls(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
}
/** /**
* Increment poll count on specified control. * Increment poll count on specified control.
* *
* Parameter port: The port ID.
* Parameter pcid: The physical controller id. * Parameter pcid: The physical controller id.
* Parameter ctrl: The control id. * Parameter ctrl: The control id.
* Return: The poll count pre-increment. * Return: The poll count pre-increment.
*/ */
uint32_t increment_polls(unsigned pcid, unsigned ctrl) throw(); uint32_t increment_polls(unsigned port, unsigned pcid, unsigned ctrl) throw();
/** /**
* Set the system flag. * Set the system flag.
*/ */
@ -495,7 +480,7 @@ public:
*/ */
bool check(const std::vector<uint32_t>& mem) throw(); bool check(const std::vector<uint32_t>& mem) throw();
private: private:
uint32_t ctrs[MAX_BUTTONS]; std::vector<uint32_t> ctrs;
bool system_flag; bool system_flag;
}; };
@ -684,17 +669,31 @@ public:
{ {
return totalsize; return totalsize;
} }
/**
* Get number of controls.
*
* Returns: The control count.
*/
unsigned maxbuttons() throw();
/**
* Do the types match?
*
* Parameter a: Another controller frame.
* Returns: True if types match, false if not.
*/
bool type_matches(const controller_frame& a);
/** /**
* Set axis/button value. * Set axis/button value.
* *
* Parameter port: The port to set.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
* Parameter ctrl: The control id. * Parameter ctrl: The control id.
* Parameter x: The new value. * Parameter x: The new value.
*/ */
void axis(unsigned pcid, unsigned ctrl, short x) throw() void axis(unsigned port, unsigned pcid, unsigned ctrl, short x) throw()
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; if(port < MAX_PORTS)
pinfo[port]->write(backing + offsets[port], pcid % MAX_CONTROLLERS_PER_PORT, ctrl, x); pinfo[port]->write(backing + offsets[port], pcid, ctrl, x);
} }
/** /**
* Set axis/button value. * Set axis/button value.
@ -702,21 +701,21 @@ public:
* Parameter idx: Control index. * Parameter idx: Control index.
* Parameter x: The new value. * Parameter x: The new value.
*/ */
void axis2(unsigned idx, short x) throw() void axis2(unsigned idx, short x) throw();
{
axis(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER, x);
}
/** /**
* Get axis/button value. * Get axis/button value.
* *
* Parameter port: The port to get.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
* Parameter ctrl: The control id. * Parameter ctrl: The control id.
* Return value: The axis value. * Return value: The axis value.
*/ */
short axis(unsigned pcid, unsigned ctrl) throw() short axis(unsigned port, unsigned pcid, unsigned ctrl) throw()
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; if(port < MAX_PORTS)
return pinfo[port]->read(backing + offsets[port], pcid % MAX_CONTROLLERS_PER_PORT, ctrl); return pinfo[port]->read(backing + offsets[port], pcid, ctrl);
else
return 0;
} }
/** /**
@ -725,31 +724,34 @@ public:
* Parameter idx: Index of control. * Parameter idx: Index of control.
* Return value: The axis value. * Return value: The axis value.
*/ */
short axis2(unsigned idx) throw() short axis2(unsigned idx) throw();
{
return axis(idx / MAX_CONTROLS_PER_CONTROLLER, idx % MAX_CONTROLS_PER_CONTROLLER);
}
/** /**
* Get controller display. * Get controller display.
* *
* Parameter port: The port to get.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
* Parameter buf: Buffer to write nul-terminated display to. * Parameter buf: Buffer to write nul-terminated display to.
*/ */
void display(unsigned pcid, char* buf) throw() void display(unsigned port, unsigned pcid, char* buf) throw()
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; if(port < MAX_PORTS)
return pinfo[port]->display(backing + offsets[port], pcid % MAX_CONTROLLERS_PER_PORT, buf); return pinfo[port]->display(backing + offsets[port], pcid, buf);
else
strcpy(buf, "");
} }
/** /**
* Get device type. * Get device type.
* *
* Parameter port: The port to get.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
* Returns: Device type. * Returns: Device type.
*/ */
devicetype_t devicetype(unsigned pcid) throw() devicetype_t devicetype(unsigned port, unsigned pcid) throw()
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; if(port < MAX_PORTS)
return pinfo[port]->devicetype(pcid % MAX_CONTROLLERS_PER_PORT); return pinfo[port]->devicetype(pcid);
else
return DT_NONE;
} }
/** /**
* Deserialize frame from text format. * Deserialize frame from text format.
@ -822,35 +824,39 @@ public:
/** /**
* Get physical button ID for physical controller ID and logical button ID. * Get physical button ID for physical controller ID and logical button ID.
* *
* Parameter port: The port to get.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
* Parameter lbid: Logical button id. * Parameter lbid: Logical button id.
* Returns: The physical button id, or -1 if no such button. * Returns: The physical button id, or -1 if no such button.
*/ */
int button_id(unsigned pcid, unsigned lbid) int button_id(unsigned port, unsigned pcid, unsigned lbid)
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; return pinfo[port]->button_id(pcid, lbid);
return pinfo[port]->button_id(pcid % MAX_CONTROLLERS_PER_PORT, lbid);
} }
/** /**
* Does the specified controller have analog function. * Does the specified controller have analog function.
* *
* Parameter port: The port to get.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
*/ */
bool is_analog(unsigned pcid) bool is_analog(unsigned port, unsigned pcid)
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; return pinfo[port]->is_analog(pcid);
return pinfo[port]->is_analog(pcid % MAX_CONTROLLERS_PER_PORT);
} }
/** /**
* Does the specified controller have mouse-type function. * Does the specified controller have mouse-type function.
* *
* Parameter port: The port to get.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
*/ */
bool is_mouse(unsigned pcid) bool is_mouse(unsigned port, unsigned pcid)
{ {
unsigned port = (pcid / MAX_CONTROLLERS_PER_PORT) % MAX_PORTS; return pinfo[port]->is_mouse(pcid);
return pinfo[port]->is_mouse(pcid % MAX_CONTROLLERS_PER_PORT);
} }
/**
* Get max number of controls in port.
*/
unsigned control_count();
private: private:
size_t totalsize; size_t totalsize;
unsigned char memory[MAXIMUM_CONTROLLER_FRAME_SIZE]; unsigned char memory[MAXIMUM_CONTROLLER_FRAME_SIZE];
@ -1034,34 +1040,25 @@ public:
* Constructor. * Constructor.
*/ */
controller_state() throw(); controller_state() throw();
/**
* Get upper bound for number of lcids.
*/
unsigned lcid_count() throw();
/** /**
* Convert lcid (Logical Controller ID) into pcid (Physical Controler ID). * Convert lcid (Logical Controller ID) into pcid (Physical Controler ID).
* *
* Parameter lcid: The logical controller ID. * Parameter lcid: The logical controller ID.
* Return: The physical controller ID, or -1 if no such controller exists. * Return: The physical controller (port, ID), or (-1, -1) if no such controller exists.
*/ */
int lcid_to_pcid(unsigned lcid) throw(); std::pair<int, int> lcid_to_pcid(unsigned lcid) throw();
/**
* Convert acid (Analog Controller ID) into pcid.
*
* Parameter acid: The analog controller ID.
* Return: The physical controller ID, or -1 if no such controller exists.
*/
int acid_to_pcid(unsigned acid) throw();
/**
* Is given acid a mouse?
*
* Parameter acid: The analog controller ID.
* Returns: True if given acid is mouse, false otherwise.
*/
bool acid_is_mouse(unsigned acid) throw();
/** /**
* Look up device type type of given pcid. * Look up device type type of given pcid.
* *
* Parameter port: The port number.
* Parameter pcid: The physical controller id. * Parameter pcid: The physical controller id.
* Returns: The type of device. * Returns: The type of device.
*/ */
devicetype_t pcid_to_type(unsigned pcid) throw(); devicetype_t pcid_to_type(unsigned port, unsigned pcid) throw();
/** /**
* Set type of port. * Set type of port.
* *
@ -1102,13 +1099,14 @@ public:
*/ */
controller_frame get_blank() throw(); controller_frame get_blank() throw();
/** /**
* Send analog input to given acid. * Send analog input to given pcid.
* *
* Parameter acid: The acid to send input to. * Parameter port: The port to send input to.
* Parameter pcid: The pcid to send input to.
* Parameter x: The x coordinate to send. * Parameter x: The x coordinate to send.
* Parameter y: The x coordinate to send. * Parameter y: The x coordinate to send.
*/ */
void analog(unsigned acid, int x, int y) throw(); void analog(unsigned port, unsigned pcid, int x, int y) throw();
/** /**
* Manipulate the reset flag. * Manipulate the reset flag.
* *
@ -1118,35 +1116,39 @@ public:
/** /**
* Manipulate autohold. * Manipulate autohold.
* *
* Parameter port: The port number.
* Parameter pcid: The physical controller ID to manipulate. * Parameter pcid: The physical controller ID to manipulate.
* Parameter pbid: The physical button ID to manipulate. * Parameter pbid: The physical button ID to manipulate.
* Parameter newstate: The new state for autohold. * Parameter newstate: The new state for autohold.
*/ */
void autohold(unsigned pcid, unsigned pbid, bool newstate) throw(); void autohold(unsigned port, unsigned pcid, unsigned pbid, bool newstate) throw();
/** /**
* Query autohold. * Query autohold.
* *
* Parameter port: The port number to query.
* Parameter pcid: The physical controller ID to query. * Parameter pcid: The physical controller ID to query.
* Parameter pbid: The physical button ID to query. * Parameter pbid: The physical button ID to query.
* Returns: The state of autohold. * Returns: The state of autohold.
*/ */
bool autohold(unsigned pcid, unsigned pbid) throw(); bool autohold(unsigned port, unsigned pcid, unsigned pbid) throw();
/** /**
* Manipulate button. * Manipulate button.
* *
* Parameter port: The port number to manipulate.
* Parameter pcid: The physical controller ID to manipulate. * Parameter pcid: The physical controller ID to manipulate.
* Parameter pbid: The physical button ID to manipulate. * Parameter pbid: The physical button ID to manipulate.
* Parameter newstate: The new state for button. * Parameter newstate: The new state for button.
*/ */
void button(unsigned pcid, unsigned pbid, bool newstate) throw(); void button(unsigned port, unsigned pcid, unsigned pbid, bool newstate) throw();
/** /**
* Query button. * Query button.
* *
* Parameter port: The port number to query.
* Parameter pcid: The physical controller ID to query. * Parameter pcid: The physical controller ID to query.
* Parameter pbid: The physical button ID to query. * Parameter pbid: The physical button ID to query.
* Returns: The state of button. * Returns: The state of button.
*/ */
bool button(unsigned pcid, unsigned pbid) throw(); bool button(unsigned port, unsigned pcid, unsigned pbid) throw();
/** /**
* Set autofire pattern. * Set autofire pattern.
* *
@ -1157,24 +1159,23 @@ public:
/** /**
* Get physical button ID for physical controller ID and logical button ID. * Get physical button ID for physical controller ID and logical button ID.
* *
* Parameter port: The port number to query.
* Parameter pcid: Physical controller id. * Parameter pcid: Physical controller id.
* Parameter lbid: Logical button id. * Parameter lbid: Logical button id.
* Returns: The physical button id, or -1 if no such button. * Returns: The physical button id, or -1 if no such button.
*/ */
int button_id(unsigned pcid, unsigned lbid) throw(); int button_id(unsigned port, unsigned pcid, unsigned lbid) throw();
/** /**
* TODO: Document. * TODO: Document.
*/ */
bool is_analog(unsigned pcid) throw(); bool is_analog(unsigned port, unsigned pcid) throw();
/** /**
* TODO: Document. * TODO: Document.
*/ */
bool is_mouse(unsigned pcid) throw(); bool is_mouse(unsigned port, unsigned pcid) throw();
private: private:
const porttype_info* porttypeinfo[MAX_PORTS]; const porttype_info* porttypeinfo[MAX_PORTS];
porttype_t porttypes[MAX_PORTS]; porttype_t porttypes[MAX_PORTS];
int analog_indices[MAX_ANALOG];
bool analog_mouse[MAX_ANALOG];
controller_frame _input; controller_frame _input;
controller_frame _autohold; controller_frame _autohold;
controller_frame _committed; controller_frame _committed;

View file

@ -152,15 +152,16 @@ public:
* *
* The default handler does nothing. * The default handler does nothing.
* *
* Parameter pid: The physical ID of controller (0-7). * Parameter port: The port number.
* Parameter ctrlnum: Physical control number (0-15). * Parameter pid: The physical ID of controller.
* Parameter ctrlnum: Physical control number.
* Parameter newstate: True if autohold is now active, false if autohold is now inactive. * Parameter newstate: True if autohold is now active, false if autohold is now inactive.
*/ */
virtual void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate); virtual void on_autohold_update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate);
/** /**
* Call all on_autohold_update() handlers. * Call all on_autohold_update() handlers.
*/ */
static void do_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate) throw(); static void do_autohold_update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate) throw();
/** /**
* Controller configuration may have been changed. * Controller configuration may have been changed.
* *

View file

@ -101,12 +101,13 @@ public:
* Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready * Reads the data ready flag. On new frame, all data ready flags are unset. On reading control, its data ready
* flag is unset. * flag is unset.
* *
* parameter port: The port ID.
* parameter pid: Physical controller id. * parameter pid: Physical controller id.
* parameter index: Control ID. * parameter index: Control ID.
* returns: The read value. * returns: The read value.
* throws std::logic_error: Invalid control index. * throws std::logic_error: Invalid control index.
*/ */
bool get_DRDY(unsigned pid, unsigned index) throw(std::logic_error); bool get_DRDY(unsigned port, unsigned pid, unsigned index) throw(std::logic_error);
/** /**
* Set all data ready flags * Set all data ready flags
@ -116,13 +117,14 @@ public:
/** /**
* Poll a control by (port, controller, index) tuple. * Poll a control by (port, controller, index) tuple.
* *
* parameter port: The port ID.
* parameter pid: Physical controller ID. * parameter pid: Physical controller ID.
* parameter index: The index of control in controller (0 to 11) * parameter index: The index of control in controller (0 to 11)
* returns: The read value * returns: The read value
* throws std::bad_alloc: Not enough memory. * throws std::bad_alloc: Not enough memory.
* throws std::logic_error: Invalid port, controller or index or before movie start. * throws std::logic_error: Invalid port, controller or index or before movie start.
*/ */
short next_input(unsigned pid, unsigned index) throw(std::bad_alloc, std::logic_error); short next_input(unsigned port, unsigned pid, unsigned index) throw(std::bad_alloc, std::logic_error);
/** /**
* Set current control values. These are read in readwrite mode. * Set current control values. These are read in readwrite mode.
@ -292,7 +294,7 @@ public:
* throws std::bad_alloc: Not enough memory. * throws std::bad_alloc: Not enough memory.
* throws std::runtime_error: Error polling for input. * throws std::runtime_error: Error polling for input.
*/ */
short input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error); short input_poll(unsigned port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error);
/** /**
* Called when movie code needs new controls snapshot. * Called when movie code needs new controls snapshot.

View file

@ -563,5 +563,7 @@ void runemufn(T fn)
* If set, queueing synchronous function produces a warning. * If set, queueing synchronous function produces a warning.
*/ */
extern volatile bool queue_synchronous_fn_warning; extern volatile bool queue_synchronous_fn_warning;
extern void* queue_synchronous_fn_stack[512];
extern size_t queue_synchronous_fn_stacksize;
#endif #endif

View file

@ -0,0 +1,7 @@
#ifndef _library__backtrace__hpp__included__
#define _library__backtrace__hpp__included__
int lsnes_backtrace(void** buffer, int size);
void lsnes_backtrace_symbols_stderr(void* const* buffer, int size);
#endif

View file

@ -32,39 +32,41 @@ namespace
//Do button action. //Do button action.
void do_button_action(unsigned ui_id, unsigned button, short newstate, bool autoh) void do_button_action(unsigned ui_id, unsigned button, short newstate, bool autoh)
{ {
int x = controls.lcid_to_pcid(ui_id); auto x = controls.lcid_to_pcid(ui_id);
if(x < 0) { if(x.first < 0) {
messages << "No such controller #" << (ui_id + 1) << std::endl; messages << "No such controller #" << (ui_id + 1) << std::endl;
return; return;
} }
int bid = controls.button_id(x, button); int bid = controls.button_id(x.first, x.second, button);
if(bid < 0) { if(bid < 0) {
messages << "Invalid button for controller type" << std::endl; messages << "Invalid button for controller type" << std::endl;
return; return;
} }
if(autoh) { if(autoh) {
controls.autohold(x, bid, controls.autohold(x, bid) ^ newstate); controls.autohold(x.first, x.second, bid, controls.autohold(x.first, x.second, bid) ^
information_dispatch::do_autohold_update(x, bid, controls.autohold(x, bid)); newstate);
information_dispatch::do_autohold_update(x.first, x.second, bid, controls.autohold(x.first,
x.second, bid));
} else } else
controls.button(x, bid, newstate); controls.button(x.first, x.second, bid, newstate);
} }
void send_analog(unsigned lcid, int32_t x, int32_t y) void send_analog(unsigned lcid, int32_t x, int32_t y)
{ {
int pcid = controls.lcid_to_pcid(lcid); auto pcid = controls.lcid_to_pcid(lcid);
if(pcid < 0) { if(pcid.first < 0) {
messages << "Controller #" << (lcid + 1) << " not present" << std::endl; messages << "Controller #" << (lcid + 1) << " not present" << std::endl;
return; return;
} }
if(controls.is_analog(pcid) < 0) { if(controls.is_analog(pcid.first, pcid.second) < 0) {
messages << "Controller #" << (lcid + 1) << " is not analog" << std::endl; messages << "Controller #" << (lcid + 1) << " is not analog" << std::endl;
return; return;
} }
auto g2 = get_framebuffer_size(); auto g2 = get_framebuffer_size();
if(controls.is_mouse(pcid)) { if(controls.is_mouse(pcid.first, pcid.second)) {
controls.analog(pcid, x - g2.first / 2, y - g2.second / 2); controls.analog(pcid.first, pcid.second, x - g2.first / 2, y - g2.second / 2);
} else } else
controls.analog(pcid, x / 2 , y / 2); controls.analog(pcid.first, pcid.second, x / 2 , y / 2);
} }
function_ptr_command<const std::string&> autofire("autofire", "Set autofire pattern", function_ptr_command<const std::string&> autofire("autofire", "Set autofire pattern",
@ -92,15 +94,15 @@ namespace
if(!buttonmap.count(button)) if(!buttonmap.count(button))
(stringfmt() << "Invalid button '" << button << "'").throwex(); (stringfmt() << "Invalid button '" << button << "'").throwex();
auto g = buttonmap[button]; auto g = buttonmap[button];
int x = controls.lcid_to_pcid(g.first); auto x = controls.lcid_to_pcid(g.first);
if(x < 0) if(x.first < 0)
(stringfmt() << "No such controller #" << (g.first + 1)). (stringfmt() << "No such controller #" << (g.first + 1)).
throwex(); throwex();
int bid = controls.button_id(x, g.second); int bid = controls.button_id(x.first, x.second, g.second);
if(bid < 0) if(bid < 0)
(stringfmt() << "Invalid button for controller type"). (stringfmt() << "Invalid button for controller type").
throwex(); throwex();
c.axis(x, bid, true); c.axis(x.first, x.second, bid, true);
fpattern = rest; fpattern = rest;
} }
new_autofire_pattern.push_back(c); new_autofire_pattern.push_back(c);

View file

@ -341,48 +341,85 @@ pollcounter_vector::pollcounter_vector() throw()
void pollcounter_vector::clear() throw() void pollcounter_vector::clear() throw()
{ {
system_flag = false; system_flag = false;
memset(ctrs, 0, sizeof(ctrs)); //FIXME: Support more controllers/ports
ctrs.resize(96);
memset(&ctrs[0], 0, sizeof(uint32_t) * ctrs.size());
} }
void pollcounter_vector::set_all_DRDY() throw() void pollcounter_vector::set_all_DRDY() throw()
{ {
for(size_t i = 0; i < MAX_BUTTONS ; i++) for(size_t i = 0; i < ctrs.size() ; i++)
ctrs[i] |= 0x80000000UL; ctrs[i] |= 0x80000000UL;
} }
#define INDEXOF(pcid, ctrl) ((pcid) * MAX_CONTROLS_PER_CONTROLLER + (ctrl)) namespace
void pollcounter_vector::clear_DRDY(unsigned pcid, unsigned ctrl) throw()
{ {
ctrs[INDEXOF(pcid, ctrl)] &= 0x7FFFFFFFUL; inline size_t INDEXOF(unsigned port, unsigned pcid, unsigned ctrl)
{
//FIXME: Support more controls.
return port * 48 + pcid * 12 + ctrl;
} }
bool pollcounter_vector::get_DRDY(unsigned pcid, unsigned ctrl) throw() inline unsigned IINDEXOF_PORT(unsigned c)
{ {
return ((ctrs[INDEXOF(pcid, ctrl)] & 0x80000000UL) != 0); //FIXME: Support more controls.
return c / 48;
}
inline unsigned IINDEXOF_PCID(unsigned c)
{
//FIXME: Support more controls.
return c / 12 % 4;
}
inline unsigned IINDEXOF_CTRL(unsigned c)
{
//FIXME: Support more controls.
return c % 12;
}
}
void pollcounter_vector::clear_DRDY(unsigned port, unsigned pcid, unsigned ctrl) throw()
{
ctrs[INDEXOF(port, pcid, ctrl)] &= 0x7FFFFFFFUL;
}
bool pollcounter_vector::get_DRDY(unsigned port, unsigned pcid, unsigned ctrl) throw()
{
return ((ctrs[INDEXOF(port, pcid, ctrl)] & 0x80000000UL) != 0);
} }
bool pollcounter_vector::has_polled() throw() bool pollcounter_vector::has_polled() throw()
{ {
uint32_t res = system_flag ? 1 : 0; uint32_t res = system_flag ? 1 : 0;
for(size_t i = 0; i < MAX_BUTTONS ; i++) for(size_t i = 0; i < ctrs.size(); i++)
res |= ctrs[i]; res |= ctrs[i];
return ((res & 0x7FFFFFFFUL) != 0); return ((res & 0x7FFFFFFFUL) != 0);
} }
uint32_t pollcounter_vector::get_polls(unsigned pcid, unsigned ctrl) throw() uint32_t pollcounter_vector::get_polls(unsigned port, unsigned pcid, unsigned ctrl) throw()
{ {
return ctrs[INDEXOF(pcid, ctrl)] & 0x7FFFFFFFUL; return ctrs[INDEXOF(port, pcid, ctrl)] & 0x7FFFFFFFUL;
} }
uint32_t pollcounter_vector::increment_polls(unsigned pcid, unsigned ctrl) throw() uint32_t pollcounter_vector::get_polls(unsigned ctrl) throw()
{ {
size_t i = INDEXOF(pcid, ctrl); return get_polls(IINDEXOF_PORT(ctrl), IINDEXOF_PCID(ctrl), IINDEXOF_CTRL(ctrl));
}
uint32_t pollcounter_vector::increment_polls(unsigned port, unsigned pcid, unsigned ctrl) throw()
{
size_t i = INDEXOF(port, pcid, ctrl);
uint32_t x = ctrs[i] & 0x7FFFFFFFUL; uint32_t x = ctrs[i] & 0x7FFFFFFFUL;
++ctrs[i]; ++ctrs[i];
return x; return x;
} }
unsigned pollcounter_vector::maxbuttons() throw()
{
return ctrs.size();
}
void pollcounter_vector::set_system() throw() void pollcounter_vector::set_system() throw()
{ {
system_flag = true; system_flag = true;
@ -396,7 +433,7 @@ bool pollcounter_vector::get_system() throw()
uint32_t pollcounter_vector::max_polls() throw() uint32_t pollcounter_vector::max_polls() throw()
{ {
uint32_t max = system_flag ? 1 : 0; uint32_t max = system_flag ? 1 : 0;
for(unsigned i = 0; i < MAX_BUTTONS; i++) { for(unsigned i = 0; i < ctrs.size(); i++) {
uint32_t tmp = ctrs[i] & 0x7FFFFFFFUL; uint32_t tmp = ctrs[i] & 0x7FFFFFFFUL;
max = (max < tmp) ? tmp : max; max = (max < tmp) ? tmp : max;
} }
@ -405,29 +442,28 @@ uint32_t pollcounter_vector::max_polls() throw()
void pollcounter_vector::save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc) void pollcounter_vector::save_state(std::vector<uint32_t>& mem) throw(std::bad_alloc)
{ {
mem.resize(4 + MAX_BUTTONS ); mem.resize(4 + ctrs.size());
//Compatiblity fun. //Compatiblity fun.
mem[0] = 0x80000000UL; mem[0] = 0x80000000UL;
mem[1] = system_flag ? 1 : 0x80000000UL; mem[1] = system_flag ? 1 : 0x80000000UL;
mem[2] = system_flag ? 1 : 0x80000000UL; mem[2] = system_flag ? 1 : 0x80000000UL;
mem[3] = system_flag ? 1 : 0x80000000UL; mem[3] = system_flag ? 1 : 0x80000000UL;
for(size_t i = 0; i < MAX_BUTTONS ; i++) for(size_t i = 0; i < ctrs.size(); i++)
mem[4 + i] = ctrs[i]; mem[4 + i] = ctrs[i];
} }
void pollcounter_vector::load_state(const std::vector<uint32_t>& mem) throw() void pollcounter_vector::load_state(const std::vector<uint32_t>& mem) throw()
{ {
system_flag = (mem[1] | mem[2] | mem[3]) & 0x7FFFFFFFUL; system_flag = (mem[1] | mem[2] | mem[3]) & 0x7FFFFFFFUL;
for(size_t i = 0; i < MAX_BUTTONS ; i++) for(size_t i = 0; i < ctrs.size(); i++)
ctrs[i] = mem[i + 4]; ctrs[i] = mem[i + 4];
} }
bool pollcounter_vector::check(const std::vector<uint32_t>& mem) throw() bool pollcounter_vector::check(const std::vector<uint32_t>& mem) throw()
{ {
return (mem.size() == MAX_BUTTONS + 4); return (mem.size() == ctrs.size() + 4);
} }
controller_frame::controller_frame(porttype_t p1, porttype_t p2) throw(std::runtime_error) controller_frame::controller_frame(porttype_t p1, porttype_t p2) throw(std::runtime_error)
{ {
memset(memory, 0, sizeof(memory)); memset(memory, 0, sizeof(memory));
@ -480,6 +516,30 @@ void controller_frame::set_types(const porttype_t* tarr)
totalsize = offset; totalsize = offset;
} }
void controller_frame::axis2(unsigned ctrl, short value) throw()
{
axis(IINDEXOF_PORT(ctrl), IINDEXOF_PCID(ctrl), IINDEXOF_CTRL(ctrl), value);
}
short controller_frame::axis2(unsigned ctrl) throw()
{
return axis(IINDEXOF_PORT(ctrl), IINDEXOF_PCID(ctrl), IINDEXOF_CTRL(ctrl));
}
unsigned controller_frame::maxbuttons() throw()
{
//FIXME: Fix for more ports/controllers/controls
return 96;
}
bool controller_frame::type_matches(const controller_frame& a)
{
for(unsigned i = 0; i < MAX_PORTS; i++)
if(a.types[i] != types[i])
return false;
return true;
}
size_t controller_frame_vector::walk_helper(size_t frame, bool sflag) throw() size_t controller_frame_vector::walk_helper(size_t frame, bool sflag) throw()
{ {
size_t ret = sflag ? frame : 0; size_t ret = sflag ? frame : 0;
@ -717,6 +777,12 @@ controller_frame::controller_frame() throw()
totalsize = SYSTEM_BYTES; totalsize = SYSTEM_BYTES;
} }
unsigned controller_frame::control_count()
{
//FIXME: More controls.
return 12;
}
void controller_frame::set_port_type(unsigned port, porttype_t ptype) throw(std::runtime_error) void controller_frame::set_port_type(unsigned port, porttype_t ptype) throw(std::runtime_error)
{ {
char tmp[MAXIMUM_CONTROLLER_FRAME_SIZE] = {0}; char tmp[MAXIMUM_CONTROLLER_FRAME_SIZE] = {0};
@ -750,59 +816,46 @@ void controller_frame::set_port_type(unsigned port, porttype_t ptype) throw(std:
controller_state::controller_state() throw() controller_state::controller_state() throw()
{ {
for(size_t i = 0; i < MAX_ANALOG; i++) {
analog_indices[i] = -1;
analog_mouse[i] = false;
}
for(size_t i = 0; i < MAX_PORTS; i++) { for(size_t i = 0; i < MAX_PORTS; i++) {
porttypes[i] = PT_INVALID; porttypes[i] = PT_INVALID;
porttypeinfo[i] = NULL; porttypeinfo[i] = NULL;
} }
} }
int controller_state::lcid_to_pcid(unsigned lcid) throw() unsigned controller_state::lcid_count() throw()
{
//FIXME: Support more ports/controllers.
return 8;
}
std::pair<int, int> controller_state::lcid_to_pcid(unsigned lcid) throw()
{ {
if(!porttypeinfo[0] || !porttypeinfo[1]) if(!porttypeinfo[0] || !porttypeinfo[1])
return -1; return std::make_pair(-1, -1);
//FIXME: Support more ports/controllers.
unsigned p1devs = porttypeinfo[0]->controllers; unsigned p1devs = porttypeinfo[0]->controllers;
unsigned p2devs = porttypeinfo[1]->controllers; unsigned p2devs = porttypeinfo[1]->controllers;
if(lcid >= p1devs + p2devs) if(lcid >= p1devs + p2devs)
return -1; return std::make_pair(-1, -1);
//Exceptional: If p1 is none, map all to p2. //Exceptional: If p1 is none, map all to p2.
if(!p1devs) if(!p1devs)
return lcid + MAX_CONTROLLERS_PER_PORT; return std::make_pair(1, lcid);
//LID 0 Is always PID 0 unless out of range. //LID 0 Is always PID 0 unless out of range.
if(lcid == 0) if(lcid == 0)
return 0; return std::make_pair(0, 0);
//LID 1-n are on port 2. //LID 1-n are on port 2.
else if(lcid < 1 + p2devs) else if(lcid < 1 + p2devs)
return lcid - 1 + MAX_CONTROLLERS_PER_PORT; return std::make_pair(1, lcid- 1);
//From there, those are on port 1 (except for the first). //From there, those are on port 1 (except for the first).
else else
return lcid - p2devs; return std::make_pair(0, lcid - p2devs);
} }
int controller_state::acid_to_pcid(unsigned acid) throw() devicetype_t controller_state::pcid_to_type(unsigned port, unsigned pcid) throw()
{ {
if(acid > MAX_ANALOG)
return -1;
return analog_indices[acid];
}
bool controller_state::acid_is_mouse(unsigned acid) throw()
{
if(acid > MAX_ANALOG)
return -1;
return analog_mouse[acid];
}
devicetype_t controller_state::pcid_to_type(unsigned pcid) throw()
{
size_t port = pcid / MAX_CONTROLLERS_PER_PORT;
if(port >= MAX_PORTS) if(port >= MAX_PORTS)
return DT_NONE; return DT_NONE;
return porttypeinfo[port]->devicetype(pcid % MAX_CONTROLLERS_PER_PORT); return porttypeinfo[port]->devicetype(pcid);
} }
controller_frame controller_state::get(uint64_t framenum) throw() controller_frame controller_state::get(uint64_t framenum) throw()
@ -813,14 +866,10 @@ controller_frame controller_state::get(uint64_t framenum) throw()
return _input ^ _autohold; return _input ^ _autohold;
} }
void controller_state::analog(unsigned acid, int x, int y) throw() void controller_state::analog(unsigned port, unsigned pcid, int x, int y) throw()
{ {
if(acid >= MAX_ANALOG || analog_indices[acid] < 0) { _input.axis(port, pcid, 0, x);
messages << "No analog controller #" << acid << std::endl; _input.axis(port, pcid, 1, y);
return;
}
_input.axis(analog_indices[acid], 0, x);
_input.axis(analog_indices[acid], 1, y);
} }
void controller_state::reset(int32_t delay) throw() void controller_state::reset(int32_t delay) throw()
@ -834,25 +883,25 @@ void controller_state::reset(int32_t delay) throw()
} }
} }
void controller_state::autohold(unsigned pcid, unsigned pbid, bool newstate) throw() void controller_state::autohold(unsigned port, unsigned pcid, unsigned pbid, bool newstate) throw()
{ {
_autohold.axis(pcid, pbid, newstate ? 1 : 0); _autohold.axis(port, pcid, pbid, newstate ? 1 : 0);
information_dispatch::do_autohold_update(pcid, pbid, newstate); information_dispatch::do_autohold_update(port, pcid, pbid, newstate);
} }
bool controller_state::autohold(unsigned pcid, unsigned pbid) throw() bool controller_state::autohold(unsigned port, unsigned pcid, unsigned pbid) throw()
{ {
return (_autohold.axis(pcid, pbid) != 0); return (_autohold.axis(port, pcid, pbid) != 0);
} }
void controller_state::button(unsigned pcid, unsigned pbid, bool newstate) throw() void controller_state::button(unsigned port, unsigned pcid, unsigned pbid, bool newstate) throw()
{ {
_input.axis(pcid, pbid, newstate ? 1 : 0); _input.axis(port, pcid, pbid, newstate ? 1 : 0);
} }
bool controller_state::button(unsigned pcid, unsigned pbid) throw() bool controller_state::button(unsigned port, unsigned pcid, unsigned pbid) throw()
{ {
return (_input.axis(pcid, pbid) != 0); return (_input.axis(port, pcid, pbid) != 0);
} }
void controller_state::autofire(std::vector<controller_frame> pattern) throw(std::bad_alloc) void controller_state::autofire(std::vector<controller_frame> pattern) throw(std::bad_alloc)
@ -860,12 +909,11 @@ void controller_state::autofire(std::vector<controller_frame> pattern) throw(std
_autofire = pattern; _autofire = pattern;
} }
int controller_state::button_id(unsigned pcid, unsigned lbid) throw() int controller_state::button_id(unsigned port, unsigned pcid, unsigned lbid) throw()
{ {
size_t port = pcid / MAX_CONTROLLERS_PER_PORT;
if(port >= MAX_PORTS) if(port >= MAX_PORTS)
return -1; return -1;
return porttypeinfo[port]->button_id(pcid % MAX_CONTROLLERS_PER_PORT, lbid); return porttypeinfo[port]->button_id(pcid, lbid);
} }
void controller_state::set_port(unsigned port, porttype_t ptype, bool set_core) throw(std::runtime_error) void controller_state::set_port(unsigned port, porttype_t ptype, bool set_core) throw(std::runtime_error)
@ -887,29 +935,6 @@ void controller_state::set_port(unsigned port, porttype_t ptype, bool set_core)
} }
porttypes[port] = ptype; porttypes[port] = ptype;
porttypeinfo[port] = info; porttypeinfo[port] = info;
int i = 0;
for(unsigned j = 0; j < MAX_ANALOG; j++)
analog_indices[j] = -1;
for(unsigned j = 0; j < MAX_PORTS * MAX_CONTROLLERS_PER_PORT; j++) {
if(!porttypeinfo[j / MAX_CONTROLLERS_PER_PORT])
continue;
devicetype_t d = porttypeinfo[j / MAX_CONTROLLERS_PER_PORT]->devicetype(j % MAX_CONTROLLERS_PER_PORT);
switch(d) {
case DT_NONE:
case DT_GAMEPAD:
break;
case DT_MOUSE:
analog_mouse[i] = true;
analog_indices[i++] = j;
break;
case DT_LIGHTGUN:
analog_mouse[i] = false;
analog_indices[i++] = j;
break;
}
if(i == MAX_ANALOG)
break;
}
information_dispatch::do_autohold_reconfigure(); information_dispatch::do_autohold_reconfigure();
} }
@ -936,12 +961,12 @@ controller_frame controller_state::commit(controller_frame controls) throw()
return _committed; return _committed;
} }
bool controller_state::is_analog(unsigned pcid) throw() bool controller_state::is_analog(unsigned port, unsigned pcid) throw()
{ {
return _input.is_analog(pcid); return _input.is_analog(port, pcid);
} }
bool controller_state::is_mouse(unsigned pcid) throw() bool controller_state::is_mouse(unsigned port, unsigned pcid) throw()
{ {
return _input.is_mouse(pcid); return _input.is_mouse(port, pcid);
} }

View file

@ -188,16 +188,16 @@ void information_dispatch::do_mode_change(bool readonly) throw()
} }
} }
void information_dispatch::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate) void information_dispatch::on_autohold_update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate)
{ {
//Do nothing. //Do nothing.
} }
void information_dispatch::do_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate) throw() void information_dispatch::do_autohold_update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate) throw()
{ {
for(auto& i : dispatch()) { for(auto& i : dispatch()) {
START_EH_BLOCK START_EH_BLOCK
i->on_autohold_update(pid, ctrlnum, newstate); i->on_autohold_update(port, pid, ctrlnum, newstate);
END_EH_BLOCK(i, "on_autohold_update"); END_EH_BLOCK(i, "on_autohold_update");
} }
} }

View file

@ -259,13 +259,13 @@ void update_movie_state()
c = movb.get_movie().get_controls(); c = movb.get_movie().get_controls();
else else
c = controls.get_committed(); c = controls.get_committed();
for(unsigned i = 0; i < 8; i++) { for(unsigned i = 0; i < controls.lcid_count(); i++) {
unsigned pindex = controls.lcid_to_pcid(i); auto pindex = controls.lcid_to_pcid(i);
devicetype_t dtype = controls.pcid_to_type(pindex); devicetype_t dtype = controls.pcid_to_type(pindex.first, pindex.second);
if(dtype == DT_NONE) if(dtype == DT_NONE)
continue; continue;
char buffer[MAX_DISPLAY_LENGTH]; char buffer[MAX_DISPLAY_LENGTH];
c.display(pindex, buffer); c.display(pindex.first, pindex.second, buffer);
char y[3] = {'P', 0, 0}; char y[3] = {'P', 0, 0};
y[1] = 49 + i; y[1] = 49 + i;
_status.set(y, buffer); _status.set(y, buffer);
@ -340,7 +340,7 @@ class my_interface : public SNES::Interface
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id)
{ {
int16_t x; int16_t x;
x = movb.input_poll(port, index, id); x = movb.input_poll(port ? 1 : 0, index, id);
lua_callback_snoop_input(port ? 1 : 0, index, id, x); lua_callback_snoop_input(port ? 1 : 0, index, id, x);
return x; return x;
} }

View file

@ -21,6 +21,9 @@ namespace
//Project IDs have to match. //Project IDs have to match.
if(old_projectid != new_projectid) if(old_projectid != new_projectid)
return false; return false;
//The controller types have to match.
if(!old_movie.blank_frame(false).type_matches(new_movie.blank_frame(false)))
return false;
//If new movie is before first frame, anything with same project_id is compatible. //If new movie is before first frame, anything with same project_id is compatible.
if(frame == 0) if(frame == 0)
return true; return true;
@ -64,7 +67,8 @@ namespace
if(old_reset != new_reset || old_delay != new_delay) if(old_reset != new_reset || old_delay != new_delay)
return false; return false;
//Then rest of the stuff. //Then rest of the stuff.
for(unsigned i = 0; i < MAX_BUTTONS; i++) { unsigned buttons = old_movie.blank_frame(false).maxbuttons();
for(unsigned i = 0; i < buttons; i++) {
uint32_t p = polls[i + 4] & 0x7FFFFFFFUL; uint32_t p = polls[i + 4] & 0x7FFFFFFFUL;
short ov = 0, nv = 0; short ov = 0, nv = 0;
for(uint32_t j = 0; j < p; j++) { for(uint32_t j = 0; j < p; j++) {
@ -136,7 +140,7 @@ controller_frame movie::get_controls() throw()
c.reset(movie_data[current_frame_first_subframe].reset()); c.reset(movie_data[current_frame_first_subframe].reset());
c.delay(movie_data[current_frame_first_subframe].delay()); c.delay(movie_data[current_frame_first_subframe].delay());
} }
for(size_t i = 0; i < MAX_BUTTONS; i++) { for(size_t i = 0; i < c.maxbuttons(); i++) {
uint32_t polls = pollcounters.get_polls(i); uint32_t polls = pollcounters.get_polls(i);
uint32_t index = (changes > polls) ? polls : changes - 1; uint32_t index = (changes > polls) ? polls : changes - 1;
c.axis2(i, movie_data[current_frame_first_subframe + index].axis2(i)); c.axis2(i, movie_data[current_frame_first_subframe + index].axis2(i));
@ -188,20 +192,20 @@ void movie::next_frame() throw(std::bad_alloc)
current_frame++; current_frame++;
} }
bool movie::get_DRDY(unsigned pid, unsigned ctrl) throw(std::logic_error) bool movie::get_DRDY(unsigned port, unsigned pid, unsigned ctrl) throw(std::logic_error)
{ {
return pollcounters.get_DRDY(pid, ctrl); return pollcounters.get_DRDY(port, pid, ctrl);
} }
short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::logic_error) short movie::next_input(unsigned port, unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::logic_error)
{ {
pollcounters.clear_DRDY(pid, ctrl); pollcounters.clear_DRDY(port, pid, ctrl);
if(readonly) { if(readonly) {
//In readonly mode... //In readonly mode...
//If at the end of the movie, return released / neutral (but also record the poll)... //If at the end of the movie, return released / neutral (but also record the poll)...
if(current_frame_first_subframe >= movie_data.size()) { if(current_frame_first_subframe >= movie_data.size()) {
pollcounters.increment_polls(pid, ctrl); pollcounters.increment_polls(port, pid, ctrl);
return 0; return 0;
} }
//Before the beginning? Somebody screwed up (but return released / neutral anyway)... //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
@ -209,10 +213,10 @@ short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::
return 0; return 0;
//Otherwise find the last valid frame of input. //Otherwise find the last valid frame of input.
uint32_t changes = count_changes(current_frame_first_subframe); uint32_t changes = count_changes(current_frame_first_subframe);
uint32_t polls = pollcounters.increment_polls(pid, ctrl); uint32_t polls = pollcounters.increment_polls(port, pid, ctrl);
uint32_t index = (changes > polls) ? polls : changes - 1; uint32_t index = (changes > polls) ? polls : changes - 1;
//debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush; //debuglog << "Frame=" << current_frame << " Subframe=" << polls << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe + index](controlindex) << " fetchrow=" << current_frame_first_subframe + index << std::endl << std::flush;
return movie_data[current_frame_first_subframe + index].axis(pid, ctrl); return movie_data[current_frame_first_subframe + index].axis(port, pid, ctrl);
} else { } else {
//Readwrite mode. //Readwrite mode.
//Before the beginning? Somebody screwed up (but return released / neutral anyway)... //Before the beginning? Somebody screwed up (but return released / neutral anyway)...
@ -223,30 +227,30 @@ short movie::next_input(unsigned pid, unsigned ctrl) throw(std::bad_alloc, std::
if(current_frame_first_subframe >= movie_data.size()) { if(current_frame_first_subframe >= movie_data.size()) {
movie_data.append(current_controls.copy(true)); movie_data.append(current_controls.copy(true));
//current_frame_first_subframe should be movie_data.size(), so it is right. //current_frame_first_subframe should be movie_data.size(), so it is right.
pollcounters.increment_polls(pid, ctrl); pollcounters.increment_polls(port, pid, ctrl);
frames_in_movie++; frames_in_movie++;
//debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush; //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << movie_data[current_frame_first_subframe](controlindex) << " fetchrow=" << current_frame_first_subframe << std::endl << std::flush;
return movie_data[current_frame_first_subframe].axis(pid, ctrl); return movie_data[current_frame_first_subframe].axis(port, pid, ctrl);
} }
short new_value = current_controls.axis(pid, ctrl); short new_value = current_controls.axis(port, pid, ctrl);
//Fortunately, we know this frame is the last one in movie_data. //Fortunately, we know this frame is the last one in movie_data.
uint32_t pollcounter = pollcounters.get_polls(pid, ctrl); uint32_t pollcounter = pollcounters.get_polls(port, pid, ctrl);
uint64_t fetchrow = movie_data.size() - 1; uint64_t fetchrow = movie_data.size() - 1;
if(current_frame_first_subframe + pollcounter < movie_data.size()) { if(current_frame_first_subframe + pollcounter < movie_data.size()) {
//The index is within existing size. Change the value and propagate to all subsequent //The index is within existing size. Change the value and propagate to all subsequent
//subframes. //subframes.
for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++) for(uint64_t i = current_frame_first_subframe + pollcounter; i < movie_data.size(); i++)
movie_data[i].axis(pid, ctrl, new_value); movie_data[i].axis(port, pid, ctrl, new_value);
fetchrow = current_frame_first_subframe + pollcounter; fetchrow = current_frame_first_subframe + pollcounter;
} else if(new_value != movie_data[movie_data.size() - 1].axis(pid, ctrl)) { } else if(new_value != movie_data[movie_data.size() - 1].axis(port, pid, ctrl)) {
//The index is not within existing size and value does not match. We need to create a new //The index is not within existing size and value does not match. We need to create a new
//subframes(s), copying the last subframe. //subframes(s), copying the last subframe.
while(current_frame_first_subframe + pollcounter >= movie_data.size()) while(current_frame_first_subframe + pollcounter >= movie_data.size())
movie_data.append(movie_data[movie_data.size() - 1].copy(false)); movie_data.append(movie_data[movie_data.size() - 1].copy(false));
fetchrow = current_frame_first_subframe + pollcounter; fetchrow = current_frame_first_subframe + pollcounter;
movie_data[current_frame_first_subframe + pollcounter].axis(pid, ctrl, new_value); movie_data[current_frame_first_subframe + pollcounter].axis(port, pid, ctrl, new_value);
} }
pollcounters.increment_polls(pid, ctrl); pollcounters.increment_polls(port, pid, ctrl);
//debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush; //debuglog << "Frame=" << current_frame << " Subframe=" << (pollcounters[controlindex] - 1) << " control=" << controlindex << " value=" << new_value << " fetchrow=" << fetchrow << std::endl << std::flush;
return new_value; return new_value;
} }
@ -353,7 +357,7 @@ void movie::readonly_mode(bool enable) throw(std::bad_alloc)
movie_data[j].delay(movie_data[current_frame_first_subframe].delay()); movie_data[j].delay(movie_data[current_frame_first_subframe].delay());
} }
//Then the other buttons. //Then the other buttons.
for(size_t i = 0; i < MAX_BUTTONS; i++) { for(size_t i = 0; i < pollcounters.maxbuttons(); i++) {
uint32_t polls = pollcounters.get_polls(i); uint32_t polls = pollcounters.get_polls(i);
polls = polls ? polls : 1; polls = polls ? polls : 1;
for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++) for(uint64_t j = current_frame_first_subframe + polls; j < next_frame_first_subframe; j++)
@ -482,14 +486,13 @@ long movie_logic::new_frame_starting(bool dont_poll) throw(std::bad_alloc, std::
return mov.get_reset_status(); return mov.get_reset_status();
} }
short movie_logic::input_poll(bool port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error) short movie_logic::input_poll(unsigned port, unsigned dev, unsigned id) throw(std::bad_alloc, std::runtime_error)
{ {
unsigned pid = port ? (dev + MAX_CONTROLLERS_PER_PORT) : dev; if(!mov.get_DRDY(port, dev, id)) {
if(!mov.get_DRDY(pid, id)) {
mov.set_controls(update_controls(true)); mov.set_controls(update_controls(true));
mov.set_all_DRDY(); mov.set_all_DRDY();
} }
int16_t in = mov.next_input(pid, id); int16_t in = mov.next_input(port, dev, id);
//debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame() //debuglog << "BSNES asking for (" << port << "," << dev << "," << id << ") (frame " << mov.get_current_frame()
// << ") giving " << in << std::endl; // << ") giving " << in << std::endl;
//debuglog.flush(); //debuglog.flush();

View file

@ -7,6 +7,7 @@
#include "core/window.hpp" #include "core/window.hpp"
#include "library/string.hpp" #include "library/string.hpp"
#include "library/minmax.hpp" #include "library/minmax.hpp"
#include "library/backtrace.hpp"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
@ -575,8 +576,16 @@ void platform::queue(const std::string& c) throw(std::bad_alloc)
void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc) void platform::queue(void (*f)(void* arg), void* arg, bool sync) throw(std::bad_alloc)
{ {
if(sync && queue_synchronous_fn_warning) if(sync && queue_synchronous_fn_warning) {
void* buffer[512];
int ss = lsnes_backtrace(buffer, 512);
std::cerr << "WARNING: Synchronous queue in callback to UI, this may deadlock!" << std::endl; std::cerr << "WARNING: Synchronous queue in callback to UI, this may deadlock!" << std::endl;
std::cerr << "UI called from:" << std::endl;
lsnes_backtrace_symbols_stderr(queue_synchronous_fn_stack, queue_synchronous_fn_stacksize);
std::cerr << "Queue called from:" << std::endl;
lsnes_backtrace_symbols_stderr(buffer, ss);
std::cerr << "----------------------------------" << std::endl;
}
init_threading(); init_threading();
mutex::holder h(*queue_lock); mutex::holder h(*queue_lock);
++next_function; ++next_function;
@ -659,3 +668,5 @@ modal_pause_holder::~modal_pause_holder()
bool platform::pausing_allowed = true; bool platform::pausing_allowed = true;
double platform::global_volume = 1.0; double platform::global_volume = 1.0;
volatile bool queue_synchronous_fn_warning; volatile bool queue_synchronous_fn_warning;
void* queue_synchronous_fn_stack[512];
size_t queue_synchronous_fn_stacksize;

25
src/library/backtrace.cpp Normal file
View file

@ -0,0 +1,25 @@
#include "library/backtrace.hpp"
#ifdef __linux__
#include <execinfo.h>
int lsnes_backtrace(void** buffer, int size)
{
return backtrace(buffer, size);
}
void lsnes_backtrace_symbols_stderr(void* const* buffer, int size)
{
backtrace_symbols_fd(buffer, size, 2);
}
#else
int lsnes_backtrace(void** buffer, int size)
{
return 0;
}
void lsnes_backtrace_symbols_stderr(void* const* buffer, int size)
{
}
#endif

View file

@ -8,23 +8,21 @@ namespace
function_ptr_luafun iset("input.set", [](lua_State* LS, const std::string& fname) -> int { function_ptr_luafun iset("input.set", [](lua_State* LS, const std::string& fname) -> int {
if(!lua_input_controllerdata) if(!lua_input_controllerdata)
return 0; return 0;
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str()); unsigned port = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
unsigned index = get_numeric_argument<unsigned>(LS, 2, fname.c_str()); unsigned controller = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
short value = get_numeric_argument<short>(LS, 3, fname.c_str()); unsigned index = get_numeric_argument<unsigned>(LS, 3, fname.c_str());
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || index > MAX_CONTROLS_PER_CONTROLLER) short value = get_numeric_argument<short>(LS, 4, fname.c_str());
return 0; lua_input_controllerdata->axis(port, controller, index, value);
lua_input_controllerdata->axis(controller, index, value);
return 0; return 0;
}); });
function_ptr_luafun iget("input.get", [](lua_State* LS, const std::string& fname) -> int { function_ptr_luafun iget("input.get", [](lua_State* LS, const std::string& fname) -> int {
if(!lua_input_controllerdata) if(!lua_input_controllerdata)
return 0; return 0;
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str()); unsigned port = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
unsigned index = get_numeric_argument<unsigned>(LS, 2, fname.c_str()); unsigned controller = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT || index > MAX_CONTROLS_PER_CONTROLLER) unsigned index = get_numeric_argument<unsigned>(LS, 3, fname.c_str());
return 0; lua_pushnumber(LS, lua_input_controllerdata->axis(port, controller, index));
lua_pushnumber(LS, lua_input_controllerdata->axis(controller, index));
return 1; return 1;
}); });
@ -32,14 +30,13 @@ namespace
if(!lua_input_controllerdata) if(!lua_input_controllerdata)
return 0; return 0;
short val; short val;
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str()); unsigned port = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT) unsigned controller = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
return 0; uint64_t base = get_numeric_argument<uint64_t>(LS, 3, fname.c_str());
uint64_t base = get_numeric_argument<uint64_t>(LS, 2, fname.c_str()); for(unsigned i = 0; i < lua_input_controllerdata->control_count(); i++) {
for(unsigned i = 0; i < MAX_CONTROLS_PER_CONTROLLER; i++) {
val = (base >> i) & 1; val = (base >> i) & 1;
get_numeric_argument<short>(LS, i + 3, val, fname.c_str()); get_numeric_argument<short>(LS, i + 4, val, fname.c_str());
lua_input_controllerdata->axis(controller, i, val); lua_input_controllerdata->axis(port, controller, i, val);
} }
return 0; return 0;
}); });
@ -47,26 +44,26 @@ namespace
function_ptr_luafun igeta("input.geta", [](lua_State* LS, const std::string& fname) -> int { function_ptr_luafun igeta("input.geta", [](lua_State* LS, const std::string& fname) -> int {
if(!lua_input_controllerdata) if(!lua_input_controllerdata)
return 0; return 0;
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str()); unsigned port = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
if(controller >= MAX_PORTS * MAX_CONTROLLERS_PER_PORT) unsigned controller = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
return 0;
uint64_t fret = 0; uint64_t fret = 0;
for(unsigned i = 0; i < MAX_CONTROLS_PER_CONTROLLER; i++) for(unsigned i = 0; i < lua_input_controllerdata->control_count(); i++)
if(lua_input_controllerdata->axis(controller, i)) if(lua_input_controllerdata->axis(port, controller, i))
fret |= (1ULL << i); fret |= (1ULL << i);
lua_pushnumber(LS, fret); lua_pushnumber(LS, fret);
for(unsigned i = 0; i < MAX_CONTROLS_PER_CONTROLLER; i++) for(unsigned i = 0; i < lua_input_controllerdata->control_count(); i++)
lua_pushnumber(LS, lua_input_controllerdata->axis(controller, i)); lua_pushnumber(LS, lua_input_controllerdata->axis(port, controller, i));
return MAX_CONTROLS_PER_CONTROLLER + 1; return lua_input_controllerdata->control_count() + 1;
}); });
function_ptr_luafun igett("input.controllertype", [](lua_State* LS, const std::string& fname) -> int { function_ptr_luafun igett("input.controllertype", [](lua_State* LS, const std::string& fname) -> int {
unsigned controller = get_numeric_argument<unsigned>(LS, 1, fname.c_str()); unsigned port = get_numeric_argument<unsigned>(LS, 1, fname.c_str());
unsigned controller = get_numeric_argument<unsigned>(LS, 2, fname.c_str());
auto& m = get_movie(); auto& m = get_movie();
controller_frame f = m.read_subframe(m.get_current_frame(), 0); controller_frame f = m.read_subframe(m.get_current_frame(), 0);
porttype_t p = f.get_port_type(controller / MAX_CONTROLLERS_PER_PORT); porttype_t p = f.get_port_type(port);
const porttype_info& i = porttype_info::lookup(p); const porttype_info& i = porttype_info::lookup(p);
if(i.controllers <= controller % MAX_CONTROLLERS_PER_PORT) if(i.controllers <= controller)
lua_pushnil(LS); lua_pushnil(LS);
else if(p == PT_NONE) else if(p == PT_NONE)
lua_pushnil(LS); lua_pushnil(LS);

View file

@ -55,7 +55,7 @@ namespace
lua_pushnumber(LS, r.delay().second); lua_pushnumber(LS, r.delay().second);
lua_settable(LS, -3); lua_settable(LS, -3);
for(size_t i = 0; i < MAX_BUTTONS; i++) { for(size_t i = 0; i < r.maxbuttons(); i++) {
lua_pushnumber(LS, i + 4); lua_pushnumber(LS, i + 4);
lua_pushnumber(LS, r.axis2(i)); lua_pushnumber(LS, r.axis2(i));
lua_settable(LS, -3); lua_settable(LS, -3);

View file

@ -14,6 +14,7 @@
#include "core/settings.hpp" #include "core/settings.hpp"
#include "core/window.hpp" #include "core/window.hpp"
#include "interface/core.hpp" #include "interface/core.hpp"
#include "library/backtrace.hpp"
#include "library/zip.hpp" #include "library/zip.hpp"
#include "platform/wxwidgets/platform.hpp" #include "platform/wxwidgets/platform.hpp"
@ -81,6 +82,8 @@ namespace
{ {
void(*fn)(void*); void(*fn)(void*);
void* arg; void* arg;
int stacksize;
void* stack[512];
}; };
std::list<ui_queue_entry> ui_queue; std::list<ui_queue_entry> ui_queue;
@ -139,6 +142,8 @@ back:
if(ui_queue.empty()) if(ui_queue.empty())
goto end; goto end;
i = ui_queue.begin(); i = ui_queue.begin();
memcpy(queue_synchronous_fn_stack, i->stack, i->stacksize * sizeof(void*));
queue_synchronous_fn_stacksize = i->stacksize;
} }
i->fn(i->arg); i->fn(i->arg);
{ {
@ -389,6 +394,7 @@ void _runuifun_async(void (*fn)(void*), void* arg)
ui_queue_entry e; ui_queue_entry e;
e.fn = fn; e.fn = fn;
e.arg = arg; e.arg = arg;
e.stacksize = lsnes_backtrace(e.stack, 512);
ui_queue.push_back(e); ui_queue.push_back(e);
auto i = ui_queue.insert(ui_queue.end(), e); auto i = ui_queue.insert(ui_queue.end(), e);
post_ui_event(UISERV_UIFUN); post_ui_event(UISERV_UIFUN);

View file

@ -28,8 +28,6 @@
#include "platform/wxwidgets/window_mainwindow.hpp" #include "platform/wxwidgets/window_mainwindow.hpp"
#include "platform/wxwidgets/window_status.hpp" #include "platform/wxwidgets/window_status.hpp"
#define MAXCONTROLLERS MAX_PORTS * MAX_CONTROLLERS_PER_PORT
extern "C" extern "C"
{ {
#ifndef UINT64_C #ifndef UINT64_C
@ -38,6 +36,9 @@ extern "C"
#include <libswscale/swscale.h> #include <libswscale/swscale.h>
} }
//FIXME: Support more ports/controllers.
#define MAXCONTROLLERS 8
enum enum
{ {
wxID_PAUSE = wxID_HIGHEST + 1, wxID_PAUSE = wxID_HIGHEST + 1,
@ -179,29 +180,29 @@ namespace
return ret; return ret;
} }
bool UI_get_autohold(unsigned pid, unsigned idx) bool UI_get_autohold(unsigned port, unsigned pid, unsigned idx)
{ {
bool ret; bool ret;
runemufn([&ret, pid, idx]() { ret = controls.autohold(pid, idx); }); runemufn([&ret, port, pid, idx]() { ret = controls.autohold(port, pid, idx); });
return ret; return ret;
} }
void UI_change_autohold(unsigned pid, unsigned idx, bool newstate) void UI_change_autohold(unsigned port, unsigned pid, unsigned idx, bool newstate)
{ {
runemufn([pid, idx, newstate]() { controls.autohold(pid, idx, newstate); }); runemufn([port, pid, idx, newstate]() { controls.autohold(port, pid, idx, newstate); });
} }
int UI_controller_index_by_logical(unsigned lid) std::pair<int, int> UI_controller_index_by_logical(unsigned lid)
{ {
int ret; std::pair<int, int> ret;
runemufn([&ret, lid]() { ret = controls.lcid_to_pcid(lid); }); runemufn([&ret, lid]() { ret = controls.lcid_to_pcid(lid); });
return ret; return ret;
} }
int UI_button_id(unsigned pcid, unsigned lidx) int UI_button_id(unsigned port, unsigned pcid, unsigned lidx)
{ {
int ret; int ret;
runemufn([&ret, pcid, lidx]() { ret = controls.button_id(pcid, lidx); }); runemufn([&ret, port, pcid, lidx]() { ret = controls.button_id(port, pcid, lidx); });
return ret; return ret;
} }
@ -221,7 +222,7 @@ namespace
void change_type(); void change_type();
bool is_dummy(); bool is_dummy();
void on_select(wxCommandEvent& e); void on_select(wxCommandEvent& e);
void update(unsigned pid, unsigned ctrlnum, bool newstate); void update(std::pair<int, int> pid, unsigned ctrlnum, bool newstate);
private: private:
unsigned our_lid; unsigned our_lid;
wxMenuItem* entries[MAX_LOGICAL_BUTTONS]; wxMenuItem* entries[MAX_LOGICAL_BUTTONS];
@ -234,10 +235,10 @@ namespace
autohold_menu(wxwin_mainwindow* win); autohold_menu(wxwin_mainwindow* win);
void reconfigure(); void reconfigure();
void on_select(wxCommandEvent& e); void on_select(wxCommandEvent& e);
void update(unsigned pid, unsigned ctrlnum, bool newstate); void update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate);
private: private:
controller_autohold_menu* menus[MAXCONTROLLERS]; std::vector<controller_autohold_menu*> menus;
wxMenuItem* entries[MAXCONTROLLERS]; std::vector<wxMenuItem*> entries;
}; };
class sound_select_menu : public wxMenu class sound_select_menu : public wxMenu
@ -262,7 +263,7 @@ namespace
void on_sound_unmute(bool unmute) throw(); void on_sound_unmute(bool unmute) throw();
void on_sound_change(const std::string& dev) throw(); void on_sound_change(const std::string& dev) throw();
void on_mode_change(bool readonly) throw(); void on_mode_change(bool readonly) throw();
void on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate); void on_autohold_update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate);
void on_autohold_reconfigure(); void on_autohold_reconfigure();
private: private:
wxwin_mainwindow* mainw; wxwin_mainwindow* mainw;
@ -284,13 +285,13 @@ namespace
void controller_autohold_menu::change_type() void controller_autohold_menu::change_type()
{ {
enabled_entries = 0; enabled_entries = 0;
int pid = controls.lcid_to_pcid(our_lid); auto pid = controls.lcid_to_pcid(our_lid);
for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) { for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
int pidx = -1; int pidx = -1;
if(pid >= 0) if(pid.first >= 0)
pidx = controls.button_id(pid, i); pidx = controls.button_id(pid.first, pid.second, i);
if(pidx >= 0) { if(pidx >= 0) {
entries[i]->Check(pid > 0 && UI_get_autohold(pid, pidx)); entries[i]->Check(pid.first > 0 && UI_get_autohold(pid.first, pid.second, pidx));
entries[i]->Enable(); entries[i]->Enable();
enabled_entries++; enabled_entries++;
} else { } else {
@ -314,41 +315,33 @@ namespace
} }
unsigned lidx = (x - wxID_AUTOHOLD_FIRST) % MAX_LOGICAL_BUTTONS; unsigned lidx = (x - wxID_AUTOHOLD_FIRST) % MAX_LOGICAL_BUTTONS;
modal_pause_holder hld; modal_pause_holder hld;
int pid = controls.lcid_to_pcid(our_lid); auto pid = controls.lcid_to_pcid(our_lid);
if(pid < 0 || !entries[lidx]) if(pid.first < 0 || !entries[lidx])
return; return;
int pidx = controls.button_id(pid, lidx); int pidx = controls.button_id(pid.first, pid.second, lidx);
if(pidx < 0) if(pidx < 0)
return; return;
//Autohold change on pid=pid, ctrlindx=idx, state //Autohold change on pid=pid, ctrlindx=idx, state
bool newstate = entries[lidx]->IsChecked(); bool newstate = entries[lidx]->IsChecked();
UI_change_autohold(pid, pidx, newstate); UI_change_autohold(pid.first, pid.second, pidx, newstate);
} }
void controller_autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate) void controller_autohold_menu::update(std::pair<int, int> pid, unsigned ctrlnum, bool newstate)
{ {
modal_pause_holder hld; modal_pause_holder hld;
int pid2 = UI_controller_index_by_logical(our_lid); auto pid2 = UI_controller_index_by_logical(our_lid);
if(pid2 < 0 || static_cast<unsigned>(pid) != pid2) if(pid2.first < 0 || pid != pid2)
return; return;
for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) { for(unsigned i = 0; i < MAX_LOGICAL_BUTTONS; i++) {
int idx = UI_button_id(pid2, i); int idx = UI_button_id(pid2.first, pid2.second, i);
if(idx < 0 || static_cast<unsigned>(idx) != ctrlnum) if(idx < 0 || static_cast<unsigned>(idx) != ctrlnum)
continue; continue;
entries[i]->Check(newstate); entries[i]->Check(newstate);
} }
} }
autohold_menu::autohold_menu(wxwin_mainwindow* win) autohold_menu::autohold_menu(wxwin_mainwindow* win)
{ {
for(unsigned i = 0; i < MAXCONTROLLERS; i++) {
std::ostringstream str;
str << "Controller #&" << (i + 1);
menus[i] = new controller_autohold_menu(i, DT_NONE);
entries[i] = AppendSubMenu(menus[i], towxstring(str.str()));
entries[i]->Enable(!menus[i]->is_dummy());
}
win->Connect(wxID_AUTOHOLD_FIRST, wxID_AUTOHOLD_LAST, wxEVT_COMMAND_MENU_SELECTED, win->Connect(wxID_AUTOHOLD_FIRST, wxID_AUTOHOLD_LAST, wxEVT_COMMAND_MENU_SELECTED,
wxCommandEventHandler(autohold_menu::on_select), NULL, this); wxCommandEventHandler(autohold_menu::on_select), NULL, this);
reconfigure(); reconfigure();
@ -357,7 +350,19 @@ namespace
void autohold_menu::reconfigure() void autohold_menu::reconfigure()
{ {
modal_pause_holder hld; modal_pause_holder hld;
for(unsigned i = 0; i < MAXCONTROLLERS; i++) { unsigned idx;
for(idx = menus.size(); controls.lcid_to_pcid(idx).first >= 0; idx++);
unsigned osize = menus.size();
menus.resize(idx);
entries.resize(idx);
for(unsigned i = osize; i < menus.size(); i++) {
std::ostringstream str;
str << "Controller #&" << (i + 1);
menus[i] = new controller_autohold_menu(i, DT_NONE);
entries[i] = AppendSubMenu(menus[i], towxstring(str.str()));
entries[i]->Enable(!menus[i]->is_dummy());
}
for(unsigned i = 0; i < menus.size(); i++) {
menus[i]->change_type(); menus[i]->change_type();
entries[i]->Enable(!menus[i]->is_dummy()); entries[i]->Enable(!menus[i]->is_dummy());
} }
@ -365,14 +370,14 @@ namespace
void autohold_menu::on_select(wxCommandEvent& e) void autohold_menu::on_select(wxCommandEvent& e)
{ {
for(unsigned i = 0; i < MAXCONTROLLERS; i++) for(unsigned i = 0; i < menus.size(); i++)
menus[i]->on_select(e); menus[i]->on_select(e);
} }
void autohold_menu::update(unsigned pid, unsigned ctrlnum, bool newstate) void autohold_menu::update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate)
{ {
for(unsigned i = 0; i < MAXCONTROLLERS; i++) for(unsigned i = 0; i < menus.size(); i++)
menus[i]->update(pid, ctrlnum, newstate); menus[i]->update(std::make_pair(port, pid), ctrlnum, newstate);
} }
sound_select_menu::sound_select_menu(wxwin_mainwindow* win) sound_select_menu::sound_select_menu(wxwin_mainwindow* win)
@ -433,9 +438,9 @@ namespace
runuifun([readonly, mainw]() { mainw->menu_check(wxID_READONLY_MODE, readonly); }); runuifun([readonly, mainw]() { mainw->menu_check(wxID_READONLY_MODE, readonly); });
} }
void broadcast_listener::on_autohold_update(unsigned pid, unsigned ctrlnum, bool newstate) void broadcast_listener::on_autohold_update(unsigned port, unsigned pid, unsigned ctrlnum, bool newstate)
{ {
runuifun([pid, ctrlnum, newstate, ahmenu]() { ahmenu->update(pid, ctrlnum, newstate); }); runuifun([port, pid, ctrlnum, newstate, ahmenu]() { ahmenu->update(port, pid, ctrlnum, newstate); });
} }
void broadcast_listener::on_autohold_reconfigure() void broadcast_listener::on_autohold_reconfigure()