From 1bc3356df5373a86905b92f2b9b07b4aa2059647 Mon Sep 17 00:00:00 2001 From: zakk4223 Date: Mon, 2 Mar 2026 00:42:55 -0500 Subject: [PATCH] Extend Button/Key remap to support chording (#1063) * Extend Button/Key remap to support chording * Add saved autofire setting to 'advanced/chord' dialog --- autofire.cpp | 35 ++- autofire.h | 5 +- gamecontroller_db.cpp | 14 +- gamecontroller_db.h | 1 + input.cpp | 572 +++++++++++++++++++++++++++++++++++++----- input.h | 39 ++- menu.cpp | 532 ++++++++++++++++++++++++++++++++++----- user_io.cpp | 4 +- 8 files changed, 1067 insertions(+), 135 deletions(-) diff --git a/autofire.cpp b/autofire.cpp index 41791f2..3f2f5c9 100644 --- a/autofire.cpp +++ b/autofire.cpp @@ -39,8 +39,10 @@ struct AutofireData { struct AutofireTable { int count; // number of entries int index[MAX_AF_CODES]; // index of matching autofire rate in autofiredata[] + bool locked[MAX_AF_CODES]; uint32_t mask[MAX_AF_CODES]; // bitmask representing which buttons this code represents uint32_t autofirecodes[MAX_AF_CODES]; // codes that have autofire set (or codes that had it set, then disabled) + }; static struct AutofireTable autofiretable[NUMPLAYERS]; // tracks autofire state per key per player @@ -49,6 +51,13 @@ static struct AutofireData autofiredata_default[MAX_AF_RATES]; // hardcoded fall static int num_af_rates_default = 0; static int num_af_rates = 0; + +int get_autofire_rate_count() +{ + return num_af_rates; +} + + static void set_autofire_name(struct AutofireData *data, const char *base_name) { float hz = data->cycle_length > 0 ? (60.0f / data->cycle_length) : 0.0f; if (base_name && base_name[0]) { @@ -68,17 +77,28 @@ int get_autofire_code_idx(int player, uint32_t code) { return 0; } +// returns the locked status for an autofire code +bool get_autofire_locked(int player, uint32_t code) +{ + for (int i = 0; i != autofiretable[player].count; i++) + { + if (autofiretable[player].autofirecodes[i] == code) + return autofiretable[player].locked[i]; + } + return false; +} // autofire structs are private to this unit, so we offer a helper to clear them void clear_autofire(int player) { memset(&autofiretable[player], 0, sizeof(AutofireTable)); } // set autofire index for a code: >0 enable, 0 disable. -void set_autofire_code(int player, uint32_t code, uint32_t mask, int index) { +void set_autofire_code(int player, uint32_t code, uint32_t mask, int index, bool locked) { for (int i = 0; i != autofiretable[player].count; i++) { if (autofiretable[player].autofirecodes[i] == code) { autofiretable[player].index[i] = index; autofiretable[player].mask[i] = mask; + autofiretable[player].locked[i] = locked; return; } } @@ -90,11 +110,15 @@ void set_autofire_code(int player, uint32_t code, uint32_t mask, int index) { autofiretable[player].autofirecodes[idx] = code; autofiretable[player].index[idx] = index; autofiretable[player].mask[idx] = mask; + autofiretable[player].locked[idx] = locked; } } + // step autofire rate; wrap to disabled at max. void inc_autofire_code(int player, uint32_t code, uint32_t mask) { + if (get_autofire_locked(player, code)) return; + int index = get_autofire_code_idx(player, code) + 1; if (index <= 0) index = 1; if (index >= num_af_rates || index < 0) index = 0; @@ -122,8 +146,8 @@ bool get_autofire_bit(int player, uint32_t code, uint32_t frame_count) { } // display-only rate lookup for ui. -const char *get_autofire_rate_hz(int player, uint32_t code) { - int rate_idx = get_autofire_code_idx(player, code); +// +const char *get_autofire_rate_hz(int rate_idx) { if (rate_idx <= 0) { return "disabled"; } @@ -133,6 +157,11 @@ const char *get_autofire_rate_hz(int player, uint32_t code) { return "disabled"; } +const char *get_autofire_rate_hz_button(int player, uint32_t code) { + int rate_idx = get_autofire_code_idx(player, code); + return get_autofire_rate_hz(rate_idx); +} + bool is_autofire_enabled(int player, uint32_t code) { return get_autofire_code_idx(player, code) > 0; } diff --git a/autofire.h b/autofire.h index 713edc2..b137065 100644 --- a/autofire.h +++ b/autofire.h @@ -3,12 +3,15 @@ #include -const char *get_autofire_rate_hz(int player, uint32_t code); +const char *get_autofire_rate_hz_button(int player, uint32_t code); +const char *get_autofire_rate_hz(int rate_idx); +int get_autofire_rate_count(); bool is_autofire_enabled(int player, uint32_t code); void clear_autofire(int player); void inc_autofire_code(int player, uint32_t code, uint32_t mask); //void autofire_tick(); bool parse_autofire_cfg(); bool get_autofire_bit(int player, uint32_t code, uint32_t frame_count); +void set_autofire_code(int player, uint32_t code, uint32_t mask, int index, bool locked = false); #endif diff --git a/gamecontroller_db.cpp b/gamecontroller_db.cpp index fab15ec..d229820 100644 --- a/gamecontroller_db.cpp +++ b/gamecontroller_db.cpp @@ -195,14 +195,15 @@ static int find_linux_code_for_button(char *btn_name, uint16_t *btn_map, uint16_ #define test_bit(bit, array) (array [bit / 8] & (1 << (bit % 8))) -static void get_ctrl_index_maps(int dev_fd, char *guid, uint16_t *btn_map, uint16_t *abs_map) +void get_ctrl_index_maps(int dev_fd, char *guid, uint16_t *btn_map, uint16_t *abs_map) { unsigned char keybits[(KEY_MAX+7) / 8]; unsigned char absbits[(ABS_MAX+7) / 8]; uint16_t btn_cnt = 0; uint16_t abs_cnt = 0; - printf("Gamecontrollerdb: mapping buttons for %s ", guid); + if (guid) + printf("Gamecontrollerdb: mapping buttons for %s ", guid); if (ioctl(dev_fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits) >= 0) { for (int i = BTN_JOYSTICK; i < KEY_MAX; i++) @@ -223,11 +224,13 @@ static void get_ctrl_index_maps(int dev_fd, char *guid, uint16_t *btn_map, uint1 btn_cnt++; } } - printf("\n"); + if (guid) + printf("\n"); } - printf("Gamecontrollerdb: mapping analog axes for %s ", guid); + if (guid) + printf("Gamecontrollerdb: mapping analog axes for %s ", guid); if (ioctl(dev_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) >= 0) { //The "correct" way is to test all the way to ABS_MAX and skip any hats the device has. @@ -253,7 +256,8 @@ static void get_ctrl_index_maps(int dev_fd, char *guid, uint16_t *btn_map, uint1 } } } - printf("\n"); + if(guid) + printf("\n"); } void gcdb_show_string_for_ctrl_map(uint16_t bustype, uint16_t vid, uint16_t pid, uint16_t version,int dev_fd, const char *name, uint32_t *cur_map) diff --git a/gamecontroller_db.h b/gamecontroller_db.h index 0010c58..42540aa 100644 --- a/gamecontroller_db.h +++ b/gamecontroller_db.h @@ -11,6 +11,7 @@ bool gcdb_map_for_controller(uint16_t bustype, uint16_t vid, uint16_t pid, uint16_t version, int dev_fd, uint32_t *fill_map); void gcdb_show_string_for_ctrl_map(uint16_t bustype, uint16_t vid, uint16_t pid, uint16_t version,int dev_fd, const char *name, uint32_t *cur_map); +void get_ctrl_index_maps(int dev_fd, char *guid, uint16_t *btn_map, uint16_t *abs_map); #endif diff --git a/input.cpp b/input.cpp index 6222342..95c141c 100644 --- a/input.cpp +++ b/input.cpp @@ -38,6 +38,8 @@ #define NUMDEV 30 #define UINPUT_NAME "MiSTer virtual input" +bool update_advanced_state(int devnum, uint16_t evcode, int evstate); + char joy_bnames[NUMBUTTONS][32] = {}; int joy_bcount = 0; static struct pollfd pool[NUMDEV + 3]; @@ -1083,6 +1085,7 @@ static int ev2archie[] = NONE //255 ??? }; + uint8_t ps2_kbd_scan_set = 2; uint32_t get_ps2_code(uint16_t key) { @@ -1151,8 +1154,6 @@ typedef struct uint8_t has_mmap; uint32_t mmap[NUMBUTTONS]; - uint8_t has_jkmap; - uint16_t jkmap[1024]; int stick_l[2]; int stick_r[2]; @@ -1206,6 +1207,10 @@ typedef struct float max_range[2]; uint32_t deadzone; + advancedButtonMap advanced_map[ADVANCED_MAP_MAX]; + advancedButtonState advanced_state[ADVANCED_MAP_MAX]; + bool has_advanced_map; + uint16_t advanced_last_pressed_keycode; } devInput; static devInput input[NUMDEV] = {}; @@ -1399,15 +1404,20 @@ static int mapping_clear; static int mapping_finish; static int mapping_set; + static int mapping_current_key = 0; static int mapping_current_dev = -1; +static advancedButtonMap *mapping_store = NULL; static uint32_t tmp_axis[4]; static int tmp_axis_n = 0; static int grabbed = 1; -void start_map_setting(int cnt, int set) +static uint32_t osd_timer = 0; +static uint32_t map_advance_timer = 0; + +void start_map_setting(int cnt, int set, advancedButtonMap *abm_store) { mapping_current_key = 0; mapping_current_dev = -1; @@ -1415,8 +1425,18 @@ void start_map_setting(int cnt, int set) mapping_button = 0; mapping = 1; mapping_set = set; - if (!mapping_set) + + if (abm_store) { + mapping_type = 3; + mapping_store = abm_store; + mapping_dev = -1; + mapping_set = (cnt == 3) ? 1 : set; + if (cnt != 3) + memset((set == 1) ? abm_store->input_codes : abm_store->output_codes, 0, sizeof(abm_store->input_codes)); + osd_timer = 0; + map_advance_timer = 0; + } else if (!mapping_set) { mapping_dev = -1; mapping_type = (cnt < 0) ? 3 : cnt ? 1 : 2; } @@ -1432,6 +1452,16 @@ void start_map_setting(int cnt, int set) user_io_kbd(KEY_ENTER, 0); } +advancedButtonMap *get_map_code_store() +{ + return mapping_store; +} + +int get_map_count() +{ + return mapping_count; +} + int get_map_set() { return mapping_set; @@ -1457,12 +1487,23 @@ int get_map_finish() return mapping_finish; } -static uint32_t osd_timer = 0; + +int get_map_dev() +{ + return mapping_dev; +} + + int get_map_cancel() { return (mapping && !is_menu() && osd_timer && CheckTimer(osd_timer)); } +int get_map_advance() +{ + return (mapping && !is_menu() && map_advance_timer && CheckTimer(map_advance_timer)); +} + static char *get_unique_mapping(int dev, int force_unique = 0) { uint32_t vidpid = (input[dev].vid << 16) | input[dev].pid; @@ -1514,17 +1555,25 @@ void finish_map_setting(int dismiss) mapping = 0; if (mapping_dev<0) return; + if (mapping_type == 3) + { + if (!dismiss) return; + if (mapping_count == 3) + { + input_advanced_clear(mapping_dev); + if (mapping_store) memset(mapping_store, 0, sizeof(advancedButtonMap)); + } else if (mapping_store) { + memset(mapping_set == 2 ? mapping_store->output_codes : mapping_store->input_codes, 0, sizeof(mapping_store->input_codes)); + } + return; + } + if (mapping_type == 2) { input[mapping_dev].has_kbdmap = 0; if (dismiss) FileDeleteConfig(get_kbdmap_name(mapping_dev)); else FileSaveConfig(get_kbdmap_name(mapping_dev), &input[mapping_dev].kbdmap, sizeof(input[mapping_dev].kbdmap)); } - else if (mapping_type == 3) - { - if (dismiss) memset(input[mapping_dev].jkmap, 0, sizeof(input[mapping_dev].jkmap)); - save_map(get_jkmap_name(mapping_dev), &input[mapping_dev].jkmap, sizeof(input[mapping_dev].jkmap)); - } else { for (int i = 0; i < NUMDEV; i++) @@ -1643,9 +1692,10 @@ static int keyrah_trans(int key, int press) return key; } -static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev); +static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev, bool menu_event = false); static int kbd_toggle = 0; + static uint32_t crtgun_timeout[NUMDEV] = {}; static unsigned char mouse_btn = 0; //emulated mouse @@ -1881,7 +1931,7 @@ static bool handle_autofire_toggle(int num, uint32_t mask, uint32_t code, char p strat += sprintf(strat, "%s\n", joy_bnames[btn-4]); } - const char *rate = get_autofire_rate_hz(num, lastcode[num]); + const char *rate = get_autofire_rate_hz_button(num, lastcode[num]); if (!strcmp(rate, "disabled")) { strat += sprintf(strat, "Autofire disabled"); @@ -1952,6 +2002,7 @@ static void set_key_state(int player, uint32_t key, bool press, uint32_t mask) } } } + if (key_states[player].count < MAX_KEY_STATES) { if (press) @@ -2150,8 +2201,7 @@ static void joy_digital(int jnum, uint32_t mask, uint32_t code, char press, int default: ev.code = (bnum == BTN_OSD) ? KEY_MENU : 0; } - - input_cb(&ev, 0, 0); + input_cb(&ev, 0, 0, true); } else if (video_fb_state()) { @@ -2444,7 +2494,6 @@ static void restore_player(int dev) input[input[dev].bind].map_shown = player[k].map_shown; } - memcpy(input[dev].jkmap, player[k].jkmap, sizeof(input[dev].jkmap)); input[dev].lightgun = player[k].lightgun; break; } @@ -2565,7 +2614,7 @@ static void assign_player(int dev, int num, int force = 0) printf("Device %s %sassigned to player %d\n", input[dev].id, force ? "forcebly " : "", input[dev].num); } -static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev) +static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int dev, bool menu_event) { if (ev->type != EV_KEY && ev->type != EV_ABS && ev->type != EV_REL) return; if (ev->type == EV_KEY && (!ev->code || ev->code == KEY_UNKNOWN)) return; @@ -2708,13 +2757,10 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int input[dev].has_map++; } - if (!input[dev].has_jkmap) + if (!input[dev].has_advanced_map) { - if (!load_map(get_jkmap_name(dev), &input[dev].jkmap, sizeof(input[dev].jkmap))) - { - memset(input[dev].jkmap, 0, sizeof(input[dev].jkmap)); - } - input[dev].has_jkmap = 1; + input_advanced_load(dev); + input[dev].has_advanced_map = true; } if (!input[dev].num) @@ -2828,7 +2874,7 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int if (old_combo != 3 && input[dev].osd_combo == 3) { osd_event = 1; - if (mapping && !is_menu()) osd_timer = GetTimer(1000); + if (mapping && !is_menu()) osd_timer = mapping_type == 3 ? ((mapping_count < 3 || mapping_set == 1) ? GetTimer(5000) : 0) : GetTimer(1000); } else if (old_combo == 3 && input[dev].osd_combo != 3) { @@ -2844,23 +2890,61 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int else { map_skip = 1; - ev->value = 1; + if (mapping_type != 3) ev->value = 1; } } osd_timer = 0; } - if (mapping && mapping_type == 3) - { - if (map_skip) - { - mapping_finish = 1; - ev->value = 0; - } - osd_event = 0; - } //mapping + + if (mapping && mapping_type == 3 && ev->type == EV_KEY) + { + bool jkm_can_intr = mapping_count == 3 && mapping_button <= 1 && mapping_set == 1; + if (mapping_dev < 0) mapping_dev = dev; + if (ev->value == 1 && ev->code == KEY_ENTER) map_skip = 1; + if (map_skip && jkm_can_intr) + mapping_finish = 1; + + if (jkm_can_intr + && (ev->code == (input[mapping_dev].mmap[SYS_BTN_MENU_FUNC] & 0xFFFF) || ev->code == KEY_F12)) + map_advance_timer = ev->value ? (ev->code == KEY_F12) ? 1 : GetTimer(5000) : 0; + + if (ev->value == 1) + { + uint16_t *code_store = mapping_set == 1 ? mapping_store->input_codes : mapping_store->output_codes; + code_store[mapping_button++] = ev->code; + mapping_current_dev = mapping_dev; + if (mapping_button == sizeof(mapping_store->input_codes)/sizeof(mapping_store->input_codes[0])) //Don't overflow, just keep replacing the last entry + mapping_button--; + if (mapping_button > 1) + osd_timer = map_advance_timer = 0; + } else if (ev->value == 0 && mapping_button > 0) { + if (ev->code == KEY_ESC && (jkm_can_intr || (mapping_count < 3 && mapping_button <= 1))) + { + osd_timer = 1; + return; + } + + if (mapping_count == 3) + { + mapping_set++; + mapping_button = 0; + if (mapping_set > 2) + { + mapping_set = 1; + input_advanced_save_entry(mapping_store, mapping_dev); + memset(mapping_store, 0, sizeof(advancedButtonMap)); + } + } else { + mapping_finish = 1; + } + osd_timer = 0; + } + return; + } + if (mapping && (mapping_dev >= 0 || ev->value) && !((mapping_type < 2 || !mapping_button) && (cancel || enter)) && input[dev].quirk != QUIRK_PDSP @@ -2933,33 +3017,6 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int } return; } - else if (mapping_type == 3) // button remap - { - if (input[dev].mmap[SYS_BTN_OSD_KTGL] == ev->code || - input[dev].mmap[SYS_BTN_OSD_KTGL + 1] == ev->code || - input[dev].mmap[SYS_BTN_OSD_KTGL + 2] == ev->code) return; - - if (ev->value == 1 && !mapping_button) - { - if (mapping_dev < 0) mapping_dev = dev; - if (mapping_dev == dev && ev->code < 1024) mapping_button = ev->code; - mapping_current_dev = mapping_dev; - } - - if (mapping_dev >= 0 && (ev->code < 256 || mapping_dev == dev) && mapping_button && mapping_button != ev->code) - { - if (ev->value == 1) - { - // Technically it's hard to map the key to button as keyboards - // are all the same while joysticks are personalized and numbered. - input[mapping_dev].jkmap[mapping_button] = ev->code; - mapping_current_dev = dev; - } - - if (ev->value == 0) mapping_button = 0; - } - return; - } else { int clear = (ev->code == KEY_F12 || ev->code == KEY_MENU || ev->code == KEY_HOMEPAGE) && !is_menu(); @@ -3213,7 +3270,6 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int switch (ev->type) { case EV_KEY: - if (ev->code < 1024 && input[dev].jkmap[ev->code] && !user_io_osd_is_visible()) ev->code = input[dev].jkmap[ev->code]; //joystick buttons, digital directions if (ev->code >= 256) @@ -3373,6 +3429,9 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int } } + if (!menu_event) + update_advanced_state(dev, ev->code, ev->value); + if (ev->code == input[dev].mmap[SYS_MS_BTN_EMU] && (ev->value <= 1) && ((!(mouse_emu & 1)) ^ (!ev->value))) { mouse_emu = ev->value ? mouse_emu | 1 : mouse_emu & ~1; @@ -3413,6 +3472,11 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int if (ev->code == 111) reset_m |= 0x100; user_io_check_reset(reset_m, (keyrah && !cfg.reset_combo) ? 1 : cfg.reset_combo); + bool send_key = true; + if (!menu_event) + { + send_key = update_advanced_state(dev, ev->code, ev->value); + } if(!user_io_osd_is_visible() && ((user_io_get_kbdemu() == EMU_JOY0) || (user_io_get_kbdemu() == EMU_JOY1)) && !video_fb_state()) { if (!kbd_toggle) @@ -3501,7 +3565,7 @@ static void input_cb(struct input_event *ev, struct input_absinfo *absinfo, int } if (ev->code == KEY_HOMEPAGE) ev->code = KEY_MENU; - user_io_kbd(ev->code, ev->value); + if (send_key) user_io_kbd(ev->code, ev->value); return; } break; @@ -5183,6 +5247,7 @@ int input_test(int getchar) while (1) { + if (cfg.rumble && !is_menu()) { for (int i = 0; i < NUMDEV; i++) @@ -5197,6 +5262,7 @@ int input_test(int getchar) } } + int return_value = poll(pool, NUMDEV + 3, timeout); if (!return_value) break; @@ -5222,7 +5288,6 @@ int input_test(int getchar) { int i = pos; - if ((pool[i].fd >= 0) && (pool[i].revents & POLLIN)) { if (!input[i].mouse) @@ -5871,6 +5936,8 @@ void key_update_frames_held() int input_poll(int getchar) { + + static int poll_cnt = 0; PROFILE_FUNCTION(); static bool autofire_cfg_parsed = false; if (!autofire_cfg_parsed) autofire_cfg_parsed = parse_autofire_cfg(); @@ -6088,3 +6155,382 @@ void parse_buttons() joy_bcount++; } } + + +static void advanced_convert_jkmap(int devnum) +{ + + int abmidx = 0; + + uint16_t jkmap[1024]; + if (load_map(get_jkmap_name(devnum), jkmap, sizeof(jkmap))) + { + for(size_t i = 0; i < sizeof(jkmap)/sizeof(jkmap[0]); i++) + { + if (jkmap[i]) + { + advancedButtonMap *abm = &input[devnum].advanced_map[abmidx++]; + memset(abm, 0, sizeof(advancedButtonMap)); + abm->input_codes[0] = i; + abm->output_codes[0] = jkmap[i]; + } + } + } +} + + +uint32_t advanced_get_btn_mask_for_code(uint16_t evcode, int devnum) +{ + uint32_t mask = 0; + + + for (uint i = 0; i < BTN_NUM; i++) + { + if (evcode == (input[devnum].map[i] & 0xFFFF)) mask |= 1 << i; + else if (evcode == (input[devnum].map[i] >> 16)) mask |= 1 << i; + } + + return mask; +} + +//Only called when the pressed state changes for an entry +static uint32_t process_abm_entry(advancedButtonMap *abm, advancedButtonState *abs, int devnum) +{ + uint32_t retmask = 0; + retmask = abm->button_mask;; + for (uint k = 0; k < sizeof(abm->output_codes)/sizeof(abm->output_codes[0]); k++) + { + if (abm->output_codes[k]) + { + uint32_t ocode = abm->output_codes[k]; + uint32_t omask = advanced_get_btn_mask_for_code(ocode, devnum); + if (!omask && ocode <= 256) //Keyboard + { + user_io_kbd(ocode, abs->pressed); + input[devnum].advanced_last_pressed_keycode = abs->pressed ? ocode : 0; + } else { + retmask |= omask; + } + } + } + return retmask; +} + +uint8_t advanced_entry_pressed_state(advancedButtonMap *abm, advancedButtonState *abs, uint16_t evcode, int evstate, int devnum) +{ + int code_cnt = 0; + int pressed_cnt = 0; + bool chord_has_osd = false; + bool is_pressed = true; + bool code_is_osd_btn = (evcode == input[devnum].mmap[SYS_BTN_OSD_KTGL+1] || evcode == input[devnum].mmap[SYS_BTN_OSD_KTGL+2]); + + for (unsigned int i = 0; i < sizeof(abm->input_codes)/sizeof(abm->input_codes[0]); i++) + { + if (!abm->input_codes[i]) + break; + + if (abm->input_codes[i] == evcode) + { + if (evstate) + abs->input_state |= 1<input_state &= ~(1<input_state & 1<input_codes[i] == input[devnum].mmap[SYS_BTN_OSD_KTGL+1] || abm->input_codes[i] == input[devnum].mmap[SYS_BTN_OSD_KTGL+2]) + chord_has_osd = true; + } + + if (code_cnt != pressed_cnt) + is_pressed = false; + + //if a chord contains the OSD button(s) they have to be first, otherwise it might + //interfere with autofire chording. + if (code_is_osd_btn && evstate && chord_has_osd && pressed_cnt > 1) + is_pressed = false; + + return (is_pressed ? 1 : 0) | ((chord_has_osd ? 1 : 0) << 1); +} + +bool update_advanced_state(int devnum, uint16_t evcode, int evstate) +{ + + bool allow_keysend = true; + + + if (evstate == 2 && input[devnum].advanced_last_pressed_keycode) + { + user_io_kbd(input[devnum].advanced_last_pressed_keycode, evstate); + return false; + } + + int pnum = input[devnum].num; + if (!input[devnum].num) { + int kbd_emu = user_io_get_kbdemu(); + if (kbd_emu == EMU_JOY0) + pnum = 1; + else if (kbd_emu == EMU_JOY1) + pnum = 2; + } + + for (uint i = 0; i < ADVANCED_MAP_MAX; i++) + { + advancedButtonMap *abm = &input[devnum].advanced_map[i]; + advancedButtonState *abs = &input[devnum].advanced_state[i]; + if (!abm->input_codes[0]) break; + + uint8_t pressed_state = advanced_entry_pressed_state(abm, abs, evcode, evstate, devnum); + bool is_pressed = pressed_state & 1; + bool chord_has_osd = pressed_state & (1 << 1); + abs->last_pressed = abs->pressed; + abs->pressed = is_pressed; + + if (abs->last_pressed != abs->pressed) + { + if (abs->pressed && chord_has_osd && pnum > 0) osd_autofire_consumed[pnum-1] = true; //reuse autofire osd inhibit + if (!abs->pressed) abs->input_btn_mask = 0; + for(size_t cidx = 0; cidx < sizeof(abm->input_codes)/sizeof(abm->input_codes[0]); cidx++) + { + int icode = abm->input_codes[cidx]; + if (!icode) break; + + if (!abs->pressed && !(abs->input_state & 1<pressed); + } else { //Joypad + if (abs->pressed) + { + joy_digital(pnum, imask, icode, 0, 0, true); + abs->input_btn_mask |= imask; + } else { + joy_digital(pnum, imask, icode, 1, 0, true); + } + } + } + allow_keysend = false; + uint32_t usemask = process_abm_entry(abm, abs, devnum); + //Update joy_adv mask, store fake button code for autofire support + joy_digital(pnum, usemask, 0x8000 | i, abs->pressed, 0, false); + if (abs->pressed && abm->autofire_idx) + { + set_autofire_code(pnum-1, 0x8000 | i, usemask, abm->autofire_idx, true); + } + } + } + + //If this return is true input_cb inhibits sending the keyboard event to the core. + //Needed so the core doesn't see a keyboard event for the last key in a chord + //(or when a single key has been remapped) + return allow_keysend; +} + +advancedButtonMap *get_advanced_map_defs(int devnum) +{ + if (devnum < 0 || devnum >= NUMDEV) + { + return NULL; + } + devnum = input[devnum].bind; + return input[devnum].advanced_map; +} + +void get_button_name_for_code(uint16_t btn_code, int devnum, char *bname, size_t bname_sz) +{ + static int last_devnum = -1; + static uint16_t btn_map[KEY_MAX - BTN_JOYSTICK] = {0}; + static uint16_t abs_map[ABS_MAX] = {0}; + if (devnum != last_devnum) + { + memset(btn_map, 0xFFFF, sizeof(btn_map)); + memset(abs_map, 0xFFFF, sizeof(abs_map)); + get_ctrl_index_maps(pool[devnum].fd, NULL, btn_map, abs_map); + last_devnum = devnum; + } + + snprintf(bname, bname_sz, "??"); + if (btn_code >= KEY_EMU) //hat/analog + { + bool is_max = btn_code & 0x1; + uint16_t axis_idx = (btn_code - KEY_EMU) >> 1; + if (axis_idx >= ABS_HAT0X && axis_idx <= ABS_HAT3Y) + { + uint8_t hat_num = (axis_idx - ABS_HAT0X)/2; + bool axis_is_y = (axis_idx - ABS_HAT0X)%2; + uint8_t hat_sub = 0; + + if (axis_is_y) hat_sub = is_max ? 4 : 1; + else hat_sub = is_max ? 2 : 8; + + if (hat_sub) + snprintf(bname,bname_sz, "h%d.%d", hat_num, hat_sub); + } else { + //Mister 'fake' analog digital inputs. + for(unsigned int j=0; j < sizeof(abs_map)/sizeof(uint16_t); j++) + { + if (abs_map[j] == axis_idx) + { + snprintf(bname, bname_sz, is_max ? "+a%d" : "-a%d", j); + break; + } + } + } + } else { + for(unsigned int j=0; j < sizeof(btn_map)/sizeof(uint16_t); j++) + { + if (btn_map[j] == 0xFFFF) break; + if (btn_map[j] == btn_code) { + snprintf(bname, bname_sz, "b%d", j); + break; + } + } + } +} + +static void input_advanced_save_filename(char *fname, size_t pathlen, int dev_num, bool def=false) +{ + char *id = get_unique_mapping(dev_num); + + if (def || is_menu()) snprintf(fname, pathlen, "advanced_input_%s%s_v1.map", id, input[dev_num].mod ? "_m" : ""); + else snprintf(fname, pathlen, "%s_advanced_input_%s%s_v1.map", user_io_get_core_name(), id, input[dev_num].mod ? "_m" : ""); +} + +void input_advanced_save(int dev_num, bool do_delete) +{ + char path[256] = {JOYMAP_DIR}; + char fname[256] = {}; + + if (dev_num >= 0) + { + dev_num = input[dev_num].bind; + int bufsize = sizeof(advancedButtonMap)*ADVANCED_MAP_MAX; + input_advanced_save_filename(fname, sizeof(fname), dev_num); + + strncat(path, fname, sizeof(path)-1); + if (do_delete) + FileDeleteConfig(path); + else + FileSaveConfig(path, input[dev_num].advanced_map, bufsize); + } +} + +void input_advanced_load(int dev_num) +{ + char path[256] = {JOYMAP_DIR}; + int bufsize = sizeof(advancedButtonMap)*ADVANCED_MAP_MAX; + uint8_t *buf = new uint8_t[bufsize]; + if (buf) + { + dev_num = input[dev_num].bind; + memset(buf, 0, bufsize); + char fname[256] = {}; + input_advanced_save_filename(fname, sizeof(fname), dev_num, false); + strncat(path, fname, sizeof(path)-1); + if (FileLoadConfig(path, buf, bufsize)) + { + memcpy(input[dev_num].advanced_map, buf, bufsize); + for(int i = 0; i < ADVANCED_MAP_MAX; i++) + { + advancedButtonMap *abm = &input[dev_num].advanced_map[i]; + if (!abm->input_codes[0]) break; + } + } else { + advanced_convert_jkmap(dev_num); + } + delete[](buf); + } +} + +//If we stored them sorted we could just use memcmp.... +advancedButtonMap *input_advanced_find_match(advancedButtonMap *newMap, int devnum) +{ + for (size_t i = 0; i < ADVANCED_MAP_MAX; i++) + { + int match_count = 0; + int jcnt = 0; + int kcnt = 0; + advancedButtonMap *abm = &input[devnum].advanced_map[i]; + if (!abm->input_codes[0]) return NULL; + for(size_t j = 0; j < 4; j++) + { + + uint16_t scode = newMap->input_codes[j]; + if (scode != 0) jcnt++; + kcnt = 0; + for(size_t k = 0; k < 4; k++) + { + uint16_t acode = abm->input_codes[k]; + if (acode != 0) kcnt++; + if (acode && acode == scode) match_count++; + } + } + if ((jcnt == kcnt) && match_count == jcnt) return abm; + } + return NULL; +} + +void input_advanced_clear(int devnum) +{ + if (devnum <0 || devnum >= NUMDEV) return; + memset(input[devnum].advanced_map, 0, sizeof(input[devnum].advanced_map)); + input_advanced_save(devnum, true); +} + +void input_advanced_delete(advancedButtonMap *todel, int devnum) +{ + if (devnum <0 || devnum >= NUMDEV) return; + if (todel < input[devnum].advanced_map || todel > &input[devnum].advanced_map[ADVANCED_MAP_MAX-1]) + { + memset(todel, 0, sizeof(advancedButtonMap)); + return; + } + + //endptr is not a valid ABM, but it is a pointer to just after the last one + //only used for address calculation in memmove. + advancedButtonMap *endptr = &input[devnum].advanced_map[ADVANCED_MAP_MAX]; + memmove(todel, todel+1, (sizeof(advancedButtonMap)*(endptr-(todel+1)))); + input_advanced_save(devnum); +} + + +void input_advanced_save_entry(advancedButtonMap *abm_entry, int devnum) +{ + if (devnum <0 || devnum >= NUMDEV || !abm_entry) return; + + if (!abm_entry->input_codes[0] || (!abm_entry->output_codes[0] && !abm_entry->button_mask)) + { + input_advanced_delete(abm_entry, devnum); + return; + } + + if (abm_entry >= input[devnum].advanced_map && abm_entry <= &input[devnum].advanced_map[ADVANCED_MAP_MAX-1]) + { + input_advanced_save(devnum); + return; + } + + advancedButtonMap *abm = input_advanced_find_match(abm_entry, devnum); + if (!abm) + { + for (uint i = 0; i < ADVANCED_MAP_MAX; i++) + { + advancedButtonMap *check_abm = &input[devnum].advanced_map[i]; + if (!check_abm->input_codes[0] && !check_abm->output_codes[0]) + { + abm = check_abm; + break; + } + } + } + if (abm) + { + memcpy(abm, abm_entry, sizeof(advancedButtonMap)); + input_advanced_save(devnum); + return; + } +} diff --git a/input.h b/input.h index 8aa7429..97c9b1d 100644 --- a/input.h +++ b/input.h @@ -73,6 +73,26 @@ #define KEY_EMU (KEY_MAX+1) + +#define ADVANCED_MAP_MAX 32 + + +typedef struct { + uint32_t button_mask; + uint16_t output_codes[4]; + uint16_t input_codes[4]; + int autofire_idx; +} advancedButtonMap; + + +typedef struct { + uint8_t input_state; + uint32_t input_btn_mask; + uint8_t pressed : 1; + uint8_t last_pressed : 1; +} advancedButtonState; + + void set_kbdled(int mask, int state); int get_kbdled(int mask); int toggle_kbdled(int mask); @@ -82,7 +102,7 @@ void input_notify_mode(); int input_poll(int getchar); int is_key_pressed(int key); -void start_map_setting(int cnt, int set = 0); +void start_map_setting(int cnt, int set = 0, advancedButtonMap *code_store = NULL); int get_map_set(); int get_map_button(); int get_map_type(); @@ -92,6 +112,10 @@ int get_map_finish(); void finish_map_setting(int dismiss); uint16_t get_map_vid(); uint16_t get_map_pid(); +int get_map_dev(); +advancedButtonMap *get_map_code_store(); +int get_map_advance(); +int get_map_count(); int has_default_map(); void send_map_cmd(int key); void reset_players(); @@ -119,5 +143,18 @@ void set_ovr_buttons(char *s, int type); #define FOR_EACH_SET_BIT(mask, bit) \ for (uint32_t _m = (mask); _m; _m &= (_m - 1)) \ for (int bit = __builtin_ctz(_m), _once = 1; _once; _once = 0) +void start_code_capture(int dnum); +void end_code_capture(); +uint16_t get_captured_code(); +int code_capture_osd_count(); +int get_last_input_dev(); +int get_dev_num(int dev); +advancedButtonMap *get_advanced_map_defs(int devnum); +void get_button_name_for_code(uint16_t btn_code, int devnum, char *bname, size_t bname_sz); +void input_advanced_save(int dev_num, bool do_delete=false); +void input_advanced_load(int dev_num); +void input_advanced_save_entry(advancedButtonMap *abm_entry, int devnum); +void input_advanced_clear(int devnum); +void input_advanced_delete(advancedButtonMap *todel, int devnum); #endif diff --git a/menu.cpp b/menu.cpp index d0bf2b0..5d5f5e8 100644 --- a/menu.cpp +++ b/menu.cpp @@ -64,6 +64,8 @@ along with this program. If not, see . #include "bootcore.h" #include "ide.h" #include "profiling.h" +#include "str_util.h" +#include "autofire.h" /*menu states*/ enum MENU @@ -110,7 +112,6 @@ enum MENU MENU_JOYRESET1, MENU_JOYKBDMAP, MENU_JOYKBDMAP1, - MENU_JOYKBDMAP2, MENU_KBDMAP, MENU_KBDMAP1, MENU_KBDMAP2, @@ -204,6 +205,15 @@ enum MENU MENU_MT32PI_MAIN1, MENU_MT32PI_MAIN2, + //Advanced Button Map + MENU_ADVANCED_MAP_LIST1, + MENU_ADVANCED_MAP_LIST2, + MENU_ADVANCED_MAP_EDIT1, + MENU_ADVANCED_MAP_EDIT2, + MENU_ADVANCED_MAP_EDIT3, + MENU_ADVANCED_MAP_EDIT4, + MENU_ADVANCED_MAP_CAPTURE1, + MENU_ADVANCED_MAP_KEYCAPTURE1, // Atari 8bit cartridge type selection MENU_ATARI8BIT_CART1, MENU_ATARI8BIT_CART2, @@ -300,6 +310,10 @@ static uint32_t fs_Options; static uint32_t fs_MenuSelect; static uint32_t fs_MenuCancel; +static advancedButtonMap abm_edit_map = {}; +static advancedButtonMap *abm_edit_ptr = NULL; +static int abm_dev_num = 0; + static char* GetExt(char *ext) { static char extlist[32]; @@ -476,6 +490,38 @@ void SelectINI() select_ini = 1; } +void build_advanced_map_code_str(uint16_t *abm_codes, size_t abm_size, char *code_str, size_t code_size, int center_size = 0) +{ + char str[128] = {}; + if(abm_codes[0]) + { + strncat(str, "[", code_size); + for (unsigned int i = 0; i < abm_size/sizeof(uint16_t); i++) + { + char cs[64] = {}; + if (!abm_codes[i]) break; + if (abm_codes[i] < 256) //keyboard + { + sprintfz(cs, "k%X", abm_codes[i]); + } else { + get_button_name_for_code((abm_codes[i] & 0x7FFF), abm_dev_num, cs, sizeof(cs)); + } + strncat(cs, ",", sizeof(cs)-1); + strncat(str, cs, code_size); + } + int code_len = strlen(str); + if (str[code_len-1] == ',') str[code_len-1] = 0; + strcat(str, "]"); + } + if (center_size) + { + snprintf(code_str, code_size, "%*s%*s", (int)(center_size+strlen(str)/2), str, (int)(center_size+strlen(str)/2), " "); + } else { + snprintf(code_str, code_size, "%s", str); + } +} + + /* the Atari core handles OSD keys competely inside the core */ static uint32_t menu_key = 0; @@ -933,6 +979,87 @@ static int gun_idx = 0; static int32_t gun_pos[4] = {}; static int page = 0; +static void menu_button_name(int button, char *buf, size_t bsize) +{ + switch(button) + { + case 0: + strncpy(buf, "Right", bsize); + break; + case 1: + strncpy(buf, "Left", bsize); + break; + case 2: + strncpy(buf, "Down", bsize); + break; + case 3: + strncpy(buf, "Up", bsize); + break; + case -1: + strncpy(buf, "(None)", bsize); + break; + default: + if ((button-4 < joy_bcount) && joy_bnames[button-4][0]) + { + strncpy(buf, joy_bnames[button-4], bsize); + } else { + snprintf(buf, bsize, "%d", button-4); + } + } +} + + +static void menu_parse_buttons() +{ + if (is_minimig()) + { + joy_bcount = 7; + strcpy(joy_bnames[0], "A(Red/Fire)"); + strcpy(joy_bnames[1], "B(Blue)"); + strcpy(joy_bnames[2], "C(Yellow)"); + strcpy(joy_bnames[3], "D(Green)"); + strcpy(joy_bnames[4], "RT"); + strcpy(joy_bnames[5], "LT"); + strcpy(joy_bnames[6], "Pause"); + } + else + { + parse_buttons(); + } +} + + +void build_advanced_map_core_btn_str(advancedButtonMap *abm, char *dest_str, size_t dest_size) +{ + int first_btn = -1; + int btn_count = 0; + for(unsigned int i = 0; i < sizeof(abm->button_mask)*8; i++) + { + if (abm->button_mask & 1< 1) snprintf(dest_str, dest_size, "(%d)", btn_count); + else menu_button_name(first_btn, dest_str, dest_size); +} + + +void build_advanced_map_summary(advancedButtonMap *abm, char *dest_str, size_t dest_size) +{ + char input_str[128] = {}; + char output_str[128] = {}; + build_advanced_map_code_str(abm->input_codes, sizeof(abm->input_codes), input_str, sizeof(input_str)); + if (abm->output_codes[0]) + { + build_advanced_map_code_str(abm->output_codes, sizeof(abm->output_codes), output_str, sizeof(output_str)); + } else { + build_advanced_map_core_btn_str(abm, output_str, sizeof(output_str)); + } + snprintf(dest_str, dest_size, "%s->%s", input_str, output_str); +} + void HandleUI(void) { PROFILE_FUNCTION(); @@ -997,6 +1124,7 @@ void HandleUI(void) static int old_volume = 0; static uint32_t lock_pass_timeout = 0; static uint32_t menu_timeout = 0; + static bool ignore_osd_release = false; static char cp_MenuCancel; @@ -1138,8 +1266,16 @@ void HandleUI(void) switch (c) { case KEY_F12: - menu = true; - menu_key_set(KEY_F12 | UPSTROKE); + if (user_io_osd_is_visible()) + { + menu = true; + ignore_osd_release = true; + } + break; + case KEY_F12 | UPSTROKE: + if (!user_io_osd_is_visible() && !ignore_osd_release) + menu = true; + ignore_osd_release = false; if(video_fb_state()) video_menu_bg(user_io_status_get("[3:1]")); video_fb_enable(0); break; @@ -2445,8 +2581,10 @@ void HandleUI(void) if (is_n64()) { uint32_t n64_crc; - if (!n64_rom_tx(selPath, idx, load_addr, n64_crc)) Info("failed to load ROM"); - else if (user_io_use_cheats() && !store_name) cheats_init(selPath, n64_crc); + if (!n64_rom_tx(selPath, idx, load_addr, n64_crc)) + Info("failed to load ROM"); + else if (user_io_use_cheats() && !store_name) + cheats_init(selPath, n64_crc); } else if (is_c64() || is_c128()) { @@ -2752,7 +2890,6 @@ void HandleUI(void) break; case 3: - start_map_setting(-1); menustate = MENU_JOYKBDMAP; menusub = 0; break; @@ -4148,76 +4285,83 @@ void HandleUI(void) break; case MENU_JOYKBDMAP: + { helptext_idx = 0; menumask = 1; menustate = MENU_JOYKBDMAP1; parentstate = MENU_JOYKBDMAP; + memset(&abm_edit_map, 0, sizeof(abm_edit_map)); + start_map_setting(3, 0, &abm_edit_map); + OsdSetTitle("Button/Key remap", 0); for (int i = 0; i < 5; i++) OsdWrite(i, "", 0, 0); OsdWrite(5, info_top, 0, 0); infowrite(6, "Supported mapping:"); infowrite( 7, ""); - infowrite( 8, "Button -> Key"); - infowrite( 9, "Button -> Button same pad"); - infowrite(10, "Key -> Key"); - infowrite(11, ""); - infowrite(12, " Menu \x16 Finish "); - infowrite(13, "Menu-hold \x16 Clear "); + infowrite( 8, " Button(s)/Key(s) -> Key(s)"); + infowrite( 9, "Button(s) -> Button(s)"); + infowrite(10, ""); + if (abm_edit_map.input_codes[0] && abm_edit_map.input_codes[0] <= 256) + { + infowrite(11, " F12 \x16 Advanced "); + infowrite(12, " Esc \x16 Clear "); + infowrite(13, "Enter \x16 Finish "); + } else { + infowrite(11, " OK-hold \x16 Advanced "); + infowrite(12, " Menu \x16 Finish "); + infowrite(13, "Menu-hold \x16 Clear "); + } OsdWrite(14, info_bottom, 0, 0); OsdWrite(OsdGetSize() - 1, " Cancel", menusub == 0, 0); break; + } - case MENU_JOYKBDMAP1: - if (!get_map_button()) - { - OsdWrite(1, " Press button/key to change", 0, 0); - if (get_map_vid()) + case MENU_JOYKBDMAP1: { - OsdWrite(2, "", 0, 0); - sprintf(s, " on device %04x:%04x", get_map_vid(), get_map_pid()); - OsdWrite(3, s, 0, 0); - } - OsdWrite(OsdGetSize() - 1, " Enter \x16 Finish, Esc \x16 Clear", menusub == 0, 0); - } - else - { - if (get_map_button() <= 256) - { - OsdWrite(1, " Press key to map to", 0, 0); - OsdWrite(2, "", 0, 0); - OsdWrite(3, " on a keyboard", 0, 0); - } - else - { - OsdWrite(1, " Press button to map to", 0, 0); - OsdWrite(2, " on the same pad", 0, 0); - OsdWrite(3, " or key on a keyboard", 0, 0); - } - OsdWrite(OsdGetSize() - 1); - } - - if (select || menu || get_map_finish() || get_map_cancel()) - { - int clear = get_map_vid() && (menu || get_map_cancel()); - finish_map_setting(clear); - menu_timeout = GetTimer(1000); - OsdWrite(1); - OsdWrite(2, clear ? " Clearing" : " Finishing"); - OsdWrite(3); - OsdWrite(OsdGetSize() - 1); - menustate = MENU_JOYKBDMAP2; - } - break; - - case MENU_JOYKBDMAP2: - if (CheckTimer(menu_timeout)) - { - menustate = MENU_COMMON1; - menusub = 3; - } - break; + int map_clear = get_map_cancel(); + abm_dev_num = get_map_dev(); + if (get_map_finish() || map_clear) + { + OsdWrite(1); + OsdWrite(2, (map_clear) ? " Clearing" : " Finishing"); + OsdWrite(3); + OsdWrite(OsdGetSize() - 1); + OsdUpdate(); + finish_map_setting(map_clear); + menustate = MENU_COMMON1; + menusub = 3; + sleep(1); + } else if (get_map_advance()) { + menustate = MENU_ADVANCED_MAP_LIST1; + menusub = 0; + finish_map_setting(0); + } else if (get_map_set() == 2) { + bool is_kbd = abm_edit_map.input_codes[0] && abm_edit_map.input_codes[0] <= 256; + if (is_kbd) + { + OsdWrite(1, " Press key(s) to map to", 0, 0); + OsdWrite(2, " on a keyboard", 0, 0); + } else { + OsdWrite(1, " Press button(s) to map to", 0, 0); + OsdWrite(2, " on the same pad", 0, 0); + OsdWrite(3, " or key(s) on a keyboard", 0, 0); + } + char str[128] = {}; + build_advanced_map_code_str(abm_edit_map.output_codes, sizeof(abm_edit_map.output_codes), str, sizeof(str), 14); + OsdWrite(is_kbd ? 3 : 4, str, 0, 0); + } else { + char str[128] = {}; + build_advanced_map_code_str(abm_edit_map.input_codes, sizeof(abm_edit_map.input_codes), str, sizeof(str), 14); + OsdWrite(1, " Press button(s) ", 0, 0); + OsdWrite(2, " or key(s) to change", 0, 0); + OsdWrite(3, str, 0, 0); + OsdWrite(4, "", 0, 0); + OsdWrite(OsdGetSize() - 1, " Esc \x16 Clear, Enter \x16 Finish", menusub == 0, 0); + } + break; + } case MENU_ABOUT1: OsdSetSize(16); menumask = 0; @@ -7034,6 +7178,274 @@ void HandleUI(void) SelectFile("", 0, SCANO_CORES, MENU_CORE_FILE_SELECTED1, cp_MenuCancel); break; + case MENU_ADVANCED_MAP_LIST1: + { + OsdSetTitle("Advanced"); + menu_parse_buttons(); + menustate = MENU_ADVANCED_MAP_LIST2; + parentstate = MENU_ADVANCED_MAP_LIST1; + while(1) + { + if (!menusub) firstmenu = 0; + adjvisible = 0; + + advancedButtonMap *abms = get_advanced_map_defs(abm_dev_num); + menumask = 0x1; + uint32_t menucnt = 1; + MenuWrite(0, " New \x16", menusub == menucnt++, 0); + + int n = 1; + size_t map_cnt = 0; + for(size_t i = 0; i < ADVANCED_MAP_MAX; i++) + { + advancedButtonMap *abm = abms+i; + if (!abm->input_codes[0]) break; + map_cnt++; + build_advanced_map_summary(abm, s, sizeof(s)); + s[27] = '\x16'; + s[28] = 0; + menumask |= 1<<(i+1); + MenuWrite(n++, s, menusub == i+1, 0); + } + MenuWrite(0, " New \x16", menusub == 0, map_cnt >= ADVANCED_MAP_MAX); + if (map_cnt >= ADVANCED_MAP_MAX) + menumask &= ~0x1; + for (; n < OsdGetSize(); n++) MenuWrite(n, "", 0, 0); + if (!adjvisible) break; + firstmenu += adjvisible; + } + break; + } + + case MENU_ADVANCED_MAP_LIST2: + { + if (select) + { + advancedButtonMap *abms = get_advanced_map_defs(abm_dev_num); + memset(&abm_edit_map, 0, sizeof(abm_edit_map)); + menustate = MENU_ADVANCED_MAP_EDIT1; + parentstate = MENU_ADVANCED_MAP_LIST1; + if (menusub == 0) { + abm_edit_ptr = &abm_edit_map; + } else { + abm_edit_ptr = &abms[menusub-1]; + } + menusub = 0; + } + + if (left || back || menu) + { + menustate = MENU_COMMON1; + input_advanced_save(abm_dev_num); + parentstate = 0; + menusub = 3; + } + break; + } + + case MENU_ADVANCED_MAP_EDIT1: + { + menustate = MENU_ADVANCED_MAP_EDIT2; + parentstate = MENU_ADVANCED_MAP_EDIT1; + menumask = 0; + firstmenu = 0; + adjvisible = 0; + bool dev_kbd = false; + if (abm_edit_ptr->input_codes[0] && abm_edit_ptr->input_codes[0] <= 256) + dev_kbd = true; + menu_parse_buttons(); + + char bname[32] = {}; + build_advanced_map_core_btn_str(abm_edit_ptr, bname, sizeof(bname)); + bool keyboard_only = dev_kbd && (user_io_get_kbdemu() == EMU_NONE); + + uint32_t n = 0; + char code_str[256] = {}; + + build_advanced_map_code_str(abm_edit_ptr->input_codes, sizeof(abm_edit_ptr->input_codes), code_str, sizeof(code_str)); + snprintf(s, sizeof(s), " Input Hotkey(s) %-20s\x16",code_str); + MenuWrite(n, s, menusub == n, 0); + menumask |= 1 << n++; + + code_str[0] = 0; + snprintf(s, sizeof(s), " Core Button(s): %-17s\x10 \x11", bname); + MenuWrite(n, s, keyboard_only ? 0 : menusub == n, keyboard_only ); + if(!keyboard_only) menumask |= 1 << n; + n++; + + build_advanced_map_code_str(abm_edit_ptr->output_codes, sizeof(abm_edit_ptr->output_codes), code_str, sizeof(code_str)); + snprintf(s, sizeof(s), " Output(s): %-20s\x16", code_str); + MenuWrite(n, s, menusub == n, 0); + menumask |= 1 << n++; + + const char *af_label = get_autofire_rate_hz(abm_edit_ptr->autofire_idx); + snprintf(s, sizeof(s), " Autofire : %-20s\x16", af_label); + MenuWrite(n, s, menusub == n, 0); + menumask |= 1 << n++; + + + MenuWrite(n, " Delete", menusub == n, 0); + menumask |= 1 << n++; + MenuWrite(n, " Done", menusub == n, 0); + menumask |= 1 << n++; + for (int i = n; i < OsdGetSize(); i++) MenuWrite(i, "", 0, 0); + break; + } + + case MENU_ADVANCED_MAP_EDIT2: + { + if (select || left || right || plus || minus) + { + menustate = MENU_ADVANCED_MAP_EDIT1; + char bname[32] = {0}; + switch(menusub) + { + case 1: + { + int mapped_button_cnt = 0; + int first_map_idx = -1; + for (uint bn = 0; bn < sizeof(abm_edit_ptr->button_mask)*8; bn++) + { + if (abm_edit_ptr->button_mask & 1<= joy_bcount) first_map_idx = 0; + menu_button_name(first_map_idx, bname, sizeof(bname)); + } while (!strncmp("-", bname, sizeof(bname) )); + abm_edit_ptr->button_mask = 1<autofire_idx++; + } else if (minus) { + abm_edit_ptr->autofire_idx--; + } + if (abm_edit_ptr->autofire_idx >= get_autofire_rate_count()) + abm_edit_ptr->autofire_idx = 0; + if (abm_edit_ptr->autofire_idx < 0) + abm_edit_ptr->autofire_idx = get_autofire_rate_count()-1; + break; + case 4: + if (select) + { + menustate = MENU_ADVANCED_MAP_LIST1; + menusub = 0; + input_advanced_delete(abm_edit_ptr, abm_dev_num); + } + break; + } + } + + if (back || menu || (menusub == 5 && select)) + { + input_advanced_save_entry(abm_edit_ptr, abm_dev_num); + menustate = MENU_ADVANCED_MAP_LIST1; + menusub = 0; + } + break; + } + case MENU_ADVANCED_MAP_EDIT3: + { + menustate = MENU_ADVANCED_MAP_EDIT4; + parentstate = MENU_ADVANCED_MAP_EDIT3; + while (1) { + menumask = 0; + uint32_t n = 0; + if (!menusub) firstmenu = 0; + adjvisible = 0; + for (int i = 0; i < joy_bcount+4; i++) + { + char bname[32]; + menu_button_name(i, bname, sizeof(bname)); + if (!strcmp("-", bname)) continue; + bool b_used = abm_edit_ptr->button_mask & 1<button_mask ^= 1<input_codes[0] > 256) + { + infowrite(5, "Press button(s) on joypad"); + infowrite(6, "or key(s) on keyboard"); + } else { + infowrite(5, ""); + infowrite(6, "Press Keyboard key(s)"); + } + infowrite(7, ""); + infowrite(8, "Esc \x16 Clear"); + infowrite(9, "Menu-hold \x16 Clear"); + OsdWrite(10, info_bottom, 0, 0); + char code_str[256] = {}; + build_advanced_map_code_str((get_map_set() == 2) ? abm_edit_ptr->output_codes : abm_edit_ptr->input_codes, sizeof(abm_edit_ptr->input_codes), code_str, sizeof(code_str), 14); + OsdWrite(11, get_map_cancel() ? " Clearing" : code_str, 0, 0); + + if (get_map_finish() || get_map_cancel()) + { + menustate = MENU_ADVANCED_MAP_EDIT1; + finish_map_setting(get_map_cancel()); + } + break; + } case MENU_ATARI8BIT_CART1: helptext_idx = 0; menumask = 0; diff --git a/user_io.cpp b/user_io.cpp index e2c47dc..89f33e2 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -4113,7 +4113,7 @@ void user_io_kbd(uint16_t key, int press) if (key == KEY_MENU) key = KEY_F12; if (key != KEY_F12 || !block_F12) { - if (osd_is_visible) menu_key_set(UPSTROKE | key); + /*if (osd_is_visible)*/ menu_key_set(UPSTROKE | key); // these modifiers should be passed to core even if OSD is open or they will get stuck! if (!osd_is_visible || key == KEY_LEFTALT || key == KEY_RIGHTALT || key == KEY_LEFTMETA || key == KEY_RIGHTMETA) send_keycode(key, press); @@ -4126,7 +4126,7 @@ void user_io_kbd(uint16_t key, int press) if (!osd_is_visible && !is_menu() && key == KEY_MENU && press == 3) open_joystick_setup(); else if ((has_menu() || osd_is_visible || (get_key_mod() & (LALT | RALT | RGUI | LGUI))) && (((key == KEY_F12) && (!is_f12_mod_needed() || (get_key_mod() & (RGUI | LGUI)))) || key == KEY_MENU)) { - block_F12 = 1; + //block_F12 = 1; if (press == 1) menu_key_set(KEY_F12); } else if (osd_is_visible)