From 77745c13fac1e8adae6e156821a87f5b96dc11df Mon Sep 17 00:00:00 2001 From: schellingb <14200249+schellingb@users.noreply.github.com> Date: Fri, 25 Oct 2024 03:03:26 +0900 Subject: [PATCH] Add action to Gamepad Mapper to shift input mapping to that of a different port while holding down the button (#521) --- dosbox_pure_libretro.cpp | 134 +++++++++++++++++------------- dosbox_pure_osd.h | 172 ++++++++++++++++++++++++++------------- 2 files changed, 192 insertions(+), 114 deletions(-) diff --git a/dosbox_pure_libretro.cpp b/dosbox_pure_libretro.cpp index a1134ee2..b579768f 100644 --- a/dosbox_pure_libretro.cpp +++ b/dosbox_pure_libretro.cpp @@ -96,10 +96,10 @@ struct DBP_InputBind { Bit8u port, device, index, id; Bit16s evt, meta; union { Bit16s lastval; Bit32u _32bitalign; }; - void Update(Bit16s val, bool is_analog_button = false); + void Update(Bit16s val); #define PORT_DEVICE_INDEX_ID(b) (*(Bit32u*)&static_cast(b)) }; -enum { DBP_MAX_PORTS = 8, DBP_JOY_ANALOG_RANGE = 0x8000 }; // analog stick range is -0x8000 to 0x8000 +enum { DBP_MAX_PORTS = 8, DBP_KEYBOARD_PORT, DBP_PORT_MASK = 0x7, DBP_SHIFT_PORT_BIT = 0x80, DBP_NO_PORT = 255, DBP_JOY_ANALOG_RANGE = 0x8000 }; // analog stick range is -0x8000 to 0x8000 static const char* DBP_KBDNAMES[] = { "None","1","2","3","4","5","6","7","8","9","0","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M", @@ -137,6 +137,7 @@ enum DBP_Event_Type : Bit8u DBPET_KEYDOWN, DBPET_KEYUP, DBPET_ONSCREENKEYBOARD, DBPET_ONSCREENKEYBOARDUP, DBPET_ACTIONWHEEL, DBPET_ACTIONWHEELUP, + DBPET_SHIFTPORT, DBPET_SHIFTPORTUP, DBPET_AXISMAPPAIR, DBPET_CHANGEMOUNTS, @@ -150,7 +151,7 @@ enum DBP_Event_Type : Bit8u _DBPET_MAX }; -//static const char* DBP_Event_Type_Names[] = { "JOY1X", "JOY1Y", "JOY2X", "JOY2Y", "JOYMX", "JOYMY", "MOUSEMOVE", "MOUSEDOWN", "MOUSEUP", "MOUSESETSPEED", "MOUSERESETSPEED", "JOYHATSETBIT", "JOYHATUNSETBIT", "JOY1DOWN", "JOY1UP", "JOY2DOWN", "JOY2UP", "KEYDOWN", "KEYUP", "ONSCREENKEYBOARD", "ONSCREENKEYBOARDUP", "ACTIONWHEEL", "ACTIONWHEELUP", "AXIS_TO_KEY", "CHANGEMOUNTS", "REFRESHSYSTEM", "MAX" }; +static const char* DBP_Event_Type_Names[] = { "JOY1X", "JOY1Y", "JOY2X", "JOY2Y", "JOYMX", "JOYMY", "MOUSEMOVE", "MOUSEDOWN", "MOUSEUP", "MOUSESETSPEED", "MOUSERESETSPEED", "JOYHATSETBIT", "JOYHATUNSETBIT", "JOY1DOWN", "JOY1UP", "JOY2DOWN", "JOY2UP", "KEYDOWN", "KEYUP", "ONSCREENKEYBOARD", "ONSCREENKEYBOARDUP", "ACTIONWHEEL", "ACTIONWHEELUP", "SHIFTPORT", "SHIFTPORTUP", "AXIS_TO_KEY", "CHANGEMOUNTS", "REFRESHSYSTEM", "MAX" }; static const char *DBPDEV_Keyboard = "Keyboard", *DBPDEV_Mouse = "Mouse", *DBPDEV_Joystick = "Joystick"; static const struct DBP_SpecialMapping { int16_t evt, meta; const char *dev, *name; } DBP_SpecialMappings[] = { @@ -181,16 +182,20 @@ static const struct DBP_SpecialMapping { int16_t evt, meta; const char *dev, *na { DBPET_JOY2X, 1, DBPDEV_Joystick, "Joy 2 Right" }, // 224 { DBPET_ONSCREENKEYBOARD, 0, NULL, "On Screen Keyboard" }, // 225 { DBPET_ACTIONWHEEL, 0, NULL, "Action Wheel" }, // 226 + { DBPET_SHIFTPORT, 0, NULL, "Port #1 while holding" }, // 227 + { DBPET_SHIFTPORT, 1, NULL, "Port #2 while holding" }, // 228 + { DBPET_SHIFTPORT, 2, NULL, "Port #3 while holding" }, // 229 + { DBPET_SHIFTPORT, 3, NULL, "Port #4 while holding" }, // 230 }; #define DBP_SPECIALMAPPING(key) DBP_SpecialMappings[(key)-DBP_SPECIALMAPPINGS_KEY] enum { DBP_SPECIALMAPPINGS_KEY = 200, DBP_SPECIALMAPPINGS_MAX = 200+(sizeof(DBP_SpecialMappings)/sizeof(DBP_SpecialMappings[0])) }; enum { DBP_SPECIALMAPPINGS_OSK = 225, DBP_SPECIALMAPPINGS_ACTIONWHEEL = 226 }; enum { DBP_EVENT_QUEUE_SIZE = 256, DBP_DOWN_COUNT_MASK = 127, DBP_DOWN_BY_KEYBOARD = 128 }; -static struct DBP_Event { DBP_Event_Type type; int val, val2; } dbp_event_queue[DBP_EVENT_QUEUE_SIZE]; +static struct DBP_Event { DBP_Event_Type type; Bit8u port; int val, val2; } dbp_event_queue[DBP_EVENT_QUEUE_SIZE]; static int dbp_event_queue_write_cursor; static int dbp_event_queue_read_cursor; static int dbp_keys_down_count; -static unsigned char dbp_keys_down[KBD_LAST+17]; +static unsigned char dbp_keys_down[KBD_LAST + 21]; static unsigned short dbp_keymap_dos2retro[KBD_LAST]; static unsigned char dbp_keymap_retro2dos[RETROK_LAST]; @@ -201,6 +206,7 @@ struct DBP_Interceptor virtual void input() = 0; virtual void close() = 0; virtual bool evnt(DBP_Event_Type type, int val, int val2) { return false; } + virtual bool usegfx() { return true; } }; static DBP_Interceptor *dbp_intercept, *dbp_intercept_next; static void DBP_SetIntercept(DBP_Interceptor* intercept) { if (!dbp_intercept) dbp_intercept = intercept; dbp_intercept_next = intercept; } @@ -294,25 +300,27 @@ void NET_SetupEthernet(); bool MIDI_TSF_SwitchSF(const char*); const char* DBP_MIDI_StartupError(Section* midisec, const char*& arg); -static void DBP_QueueEvent(DBP_Event_Type type, int val = 0, int val2 = 0) +static void DBP_QueueEvent(DBP_Event_Type type, Bit8u port, int val = 0, int val2 = 0) { unsigned char* downs = dbp_keys_down; switch (type) { case DBPET_KEYDOWN: DBP_ASSERT(val > KBD_NONE && val < KBD_LAST); goto check_down; case DBPET_KEYUP: DBP_ASSERT(val > KBD_NONE && val < KBD_LAST); goto check_up; - case DBPET_MOUSEDOWN: downs += KBD_LAST + 0; goto check_down; - case DBPET_MOUSEUP: downs += KBD_LAST + 0; goto check_up; - case DBPET_JOY1DOWN: downs += KBD_LAST + 3; goto check_down; - case DBPET_JOY1UP: downs += KBD_LAST + 3; goto check_up; - case DBPET_JOY2DOWN: downs += KBD_LAST + 5; goto check_down; - case DBPET_JOY2UP: downs += KBD_LAST + 5; goto check_up; - case DBPET_JOYHATSETBIT: downs += KBD_LAST + 7; goto check_down; - case DBPET_JOYHATUNSETBIT: downs += KBD_LAST + 7; goto check_up; - case DBPET_ONSCREENKEYBOARD: downs += KBD_LAST + 15; goto check_down; - case DBPET_ONSCREENKEYBOARDUP: downs += KBD_LAST + 15; goto check_up; - case DBPET_ACTIONWHEEL: downs += KBD_LAST + 16; goto check_down; - case DBPET_ACTIONWHEELUP: downs += KBD_LAST + 16; goto check_up; + case DBPET_MOUSEDOWN: DBP_ASSERT(val >= 0 && val < 3); downs += KBD_LAST + 0; goto check_down; + case DBPET_MOUSEUP: DBP_ASSERT(val >= 0 && val < 3); downs += KBD_LAST + 0; goto check_up; + case DBPET_JOY1DOWN: DBP_ASSERT(val >= 0 && val < 2); downs += KBD_LAST + 3; goto check_down; + case DBPET_JOY1UP: DBP_ASSERT(val >= 0 && val < 2); downs += KBD_LAST + 3; goto check_up; + case DBPET_JOY2DOWN: DBP_ASSERT(val >= 0 && val < 2); downs += KBD_LAST + 5; goto check_down; + case DBPET_JOY2UP: DBP_ASSERT(val >= 0 && val < 2); downs += KBD_LAST + 5; goto check_up; + case DBPET_JOYHATSETBIT: DBP_ASSERT(val >= 0 && val < 8); downs += KBD_LAST + 7; goto check_down; + case DBPET_JOYHATUNSETBIT: DBP_ASSERT(val >= 0 && val < 8); downs += KBD_LAST + 7; goto check_up; + case DBPET_ONSCREENKEYBOARD: DBP_ASSERT(val >= 0 && val < 1); downs += KBD_LAST + 15; goto check_down; + case DBPET_ONSCREENKEYBOARDUP: DBP_ASSERT(val >= 0 && val < 1); downs += KBD_LAST + 15; goto check_up; + case DBPET_ACTIONWHEEL: DBP_ASSERT(val >= 0 && val < 1); downs += KBD_LAST + 16; goto check_down; + case DBPET_ACTIONWHEELUP: DBP_ASSERT(val >= 0 && val < 1); downs += KBD_LAST + 16; goto check_up; + case DBPET_SHIFTPORT: DBP_ASSERT(val >= 0 && val < 4); downs += KBD_LAST + 17; goto check_down; + case DBPET_SHIFTPORTUP: DBP_ASSERT(val >= 0 && val < 4); downs += KBD_LAST + 17; goto check_up; check_down: if (((++downs[val]) & DBP_DOWN_COUNT_MASK) > 1) return; @@ -346,7 +354,7 @@ static void DBP_QueueEvent(DBP_Event_Type type, int val = 0, int val2 = 0) found_axis_value:break; default:; } - DBP_Event evt = { type, val, val2 }; + DBP_Event evt = { type, port, val, val2 }; DBP_ASSERT(evt.type != DBPET_AXISMAPPAIR); int cur = dbp_event_queue_write_cursor, next = ((cur + 1) % DBP_EVENT_QUEUE_SIZE); if (next == dbp_event_queue_read_cursor) @@ -387,7 +395,7 @@ static void DBP_QueueEvent(DBP_Event_Type type, int val = 0, int val2 = 0) static void DBP_ReleaseKeyEvents(bool onlyPhysicalKeys) { - for (Bit8u i = KBD_NONE + 1, iEnd = (onlyPhysicalKeys ? KBD_LAST : KBD_LAST + 17); i != iEnd; i++) + for (Bit8u i = KBD_NONE + 1, iEnd = (onlyPhysicalKeys ? KBD_LAST : KBD_LAST + 21); i != iEnd; i++) { if (!dbp_keys_down[i] || (onlyPhysicalKeys && (!(dbp_keys_down[i] & DBP_DOWN_BY_KEYBOARD) || input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0, dbp_keymap_dos2retro[i])))) continue; dbp_keys_down[i] = 1; @@ -398,27 +406,28 @@ static void DBP_ReleaseKeyEvents(bool onlyPhysicalKeys) else if (i < KBD_LAST + 7) { val -= KBD_LAST + 5; type = DBPET_JOY2UP; } else if (i < KBD_LAST + 15) { val -= KBD_LAST + 7; type = DBPET_JOYHATUNSETBIT; } else if (i < KBD_LAST + 16) { val -= KBD_LAST + 15; type = DBPET_ONSCREENKEYBOARDUP; } - else { val -= KBD_LAST + 16; type = DBPET_ACTIONWHEELUP; } - DBP_QueueEvent(type, val); + else if (i < KBD_LAST + 17) { val -= KBD_LAST + 16; type = DBPET_ACTIONWHEELUP; } + else { val -= KBD_LAST + 17; type = DBPET_SHIFTPORTUP; } + DBP_QueueEvent(type, DBP_NO_PORT, val); } } -void DBP_InputBind::Update(Bit16s val, bool is_analog_button) +void DBP_InputBind::Update(Bit16s val) { Bit16s prevval = lastval; lastval = val; // set before calling DBP_QueueEvent if (evt <= _DBPET_JOY_AXIS_MAX) { // handle analog axis mapped to analog functions - if (device == RETRO_DEVICE_JOYPAD && !is_analog_button) { lastval = prevval; return; } // handled by dbp_analog_buttons + if (device == RETRO_DEVICE_JOYPAD && (!dbp_analog_buttons || device != RETRO_DEVICE_JOYPAD || evt > _DBPET_JOY_AXIS_MAX)) { lastval = prevval; return; } // handled by dbp_analog_buttons DBP_ASSERT(device == RETRO_DEVICE_JOYPAD || meta == 0); // analog axis mapped to analog functions should always have 0 in meta DBP_ASSERT(device != RETRO_DEVICE_JOYPAD || meta == 1 || meta == -1); // buttons mapped to analog functions should always have 1 or -1 in meta - DBP_QueueEvent((DBP_Event_Type)evt, (meta ? val * meta : val), 0); + DBP_QueueEvent((DBP_Event_Type)evt, port, (meta ? val * meta : val), 0); } else if (device != RETRO_DEVICE_ANALOG) { // if button is pressed, send the _DOWN, otherwise send _UP - DBP_QueueEvent((DBP_Event_Type)(val ? evt : evt + 1), meta, port); + DBP_QueueEvent((DBP_Event_Type)(val ? evt : evt + 1), port, meta); } else for (Bit16s dir = 1; dir >= -1; dir -= 2) { @@ -427,16 +436,16 @@ void DBP_InputBind::Update(Bit16s val, bool is_analog_button) if (map == KBD_NONE) continue; if (map < KBD_LAST) { - if (dirval >= 12000 && dirprevval < 12000) DBP_QueueEvent(DBPET_KEYDOWN, map, port); - if (dirval < 12000 && dirprevval >= 12000) DBP_QueueEvent(DBPET_KEYUP, map, port); + if (dirval >= 12000 && dirprevval < 12000) DBP_QueueEvent(DBPET_KEYDOWN, port, map); + if (dirval < 12000 && dirprevval >= 12000) DBP_QueueEvent(DBPET_KEYUP, port, map); continue; } if (map < DBP_SPECIALMAPPINGS_KEY) { DBP_ASSERT(false); continue; } if (dirval <= 0 && dirprevval <= 0) continue; const DBP_SpecialMapping& sm = DBP_SPECIALMAPPING(map); - if (sm.evt <= _DBPET_JOY_AXIS_MAX) DBP_QueueEvent((DBP_Event_Type)sm.evt, (dirval < 0 ? 0 : dirval) * sm.meta, 0); - else if (dirval >= 12000 && dirprevval < 12000) DBP_QueueEvent((DBP_Event_Type)(sm.evt ), sm.meta, port); - else if (dirval < 12000 && dirprevval >= 12000) DBP_QueueEvent((DBP_Event_Type)(sm.evt + 1), sm.meta, port); + if (sm.evt <= _DBPET_JOY_AXIS_MAX) DBP_QueueEvent((DBP_Event_Type)sm.evt, port, (dirval < 0 ? 0 : dirval) * sm.meta); + else if (dirval >= 12000 && dirprevval < 12000) DBP_QueueEvent((DBP_Event_Type)(sm.evt ), port, sm.meta); + else if (dirval < 12000 && dirprevval >= 12000) DBP_QueueEvent((DBP_Event_Type)(sm.evt + 1), port, sm.meta); } } @@ -1114,13 +1123,20 @@ struct DBP_PadMapping enum EPortMode : Bit8u { MODE_DISABLED, MODE_MAPPER, MODE_PRESET_AUTOMAPPED, MODE_PRESET_GENERICKEYBOARD, MODE_PRESET_LAST = MODE_PRESET_AUTOMAPPED + (PRESET_CUSTOM - PRESET_AUTOMAPPED) - 1, MODE_KEYBOARD, MODE_KEYBOARD_MOUSE1, MODE_KEYBOARD_MOUSE2 }; INLINE static EPreset DefaultPreset(Bit8u port) { return ((port || !dbp_auto_mapping) ? PRESET_GENERICKEYBOARD : PRESET_AUTOMAPPED); } - INLINE static bool IsCustomized(Bit8u port) { return (dbp_port_mode[port] == MODE_MAPPER && GetPreset(port, DefaultPreset(port)) == PRESET_CUSTOM); } + INLINE static bool IsCustomized(Bit8u port) { return (CalcPortMode(port) == MODE_MAPPER && GetPreset(port, DefaultPreset(port)) == PRESET_CUSTOM); } INLINE static const char* GetPortPresetName(Bit8u port) { return GetPresetName(GetPreset(port)); } INLINE static void FillGenericKeys(Bit8u port) { Apply(port, PresetBinds(PRESET_GENERICKEYBOARD, port), true, true); } INLINE static void SetPreset(Bit8u port, EPreset preset) { ClearBinds(port); Apply(port, PresetBinds(preset, port), true); } INLINE static const char* GetKeyAutoMapButtonLabel(Bit8u key) { return FindAutoMapButtonLabel(1, &key); } INLINE static const char* GetWheelAutoMapButtonLabel(const DBP_WheelItem& wheel_item) { return FindAutoMapButtonLabel(wheel_item.key_count, wheel_item.k); } + static Bit8u CalcPortMode(Bit8u port) + { + if (Bit8u m = dbp_port_mode[port]) return m; + for (DBP_InputBind& b : dbp_input_binds) if (b.evt == DBPET_SHIFTPORT && b.meta == port) return MODE_MAPPER; + return MODE_DISABLED; + } + static void Load() { DOS_File *padmap = nullptr; @@ -1288,7 +1304,8 @@ struct DBP_PadMapping if (port >= DBP_MAX_PORTS || dbp_port_mode[port] == mode) return; dbp_port_mode[port] = mode; if (dbp_state <= DBPSTATE_SHUTDOWN) return; - if (mode != MODE_DISABLED) SetInputDescriptors(true); else ClearBinds((Bit8u)port); + if (mode) SetInputDescriptors(true); + else if (!CalcPortMode(port)) ClearBinds((Bit8u)port); } static void SetInputDescriptors(bool regenerate_bindings = false) @@ -1312,9 +1329,9 @@ struct DBP_PadMapping } } const Bit8u* mapping = (!dbp_custom_mapping.empty() ? &dbp_custom_mapping[0] : NULL), *mapping_end = mapping + dbp_custom_mapping.size(); - for (Bit8u port = 0; port != DBP_MAX_PORTS; port++) + for (Bit8u port = 0, mode; port != DBP_MAX_PORTS; port++) { - if (dbp_port_mode[port] == MODE_MAPPER) + if ((mode = CalcPortMode(port)) == MODE_MAPPER) { if (mapping && mapping < mapping_end) mapping = Apply(port, mapping, false); else if (port == 0 && dbp_auto_mapping) Apply(port, dbp_auto_mapping, true); @@ -1323,10 +1340,10 @@ struct DBP_PadMapping else { if (mapping && mapping < mapping_end) mapping = SkipMapping(mapping); - bool preset_mode = (dbp_port_mode[port] >= MODE_PRESET_AUTOMAPPED && dbp_port_mode[port] <= MODE_PRESET_LAST), bind_osd = (dbp_port_mode[port] != MODE_DISABLED); - EPreset preset = (preset_mode) ? (EPreset)(PRESET_AUTOMAPPED + (dbp_port_mode[port] - MODE_PRESET_AUTOMAPPED)) - : (dbp_port_mode[port] == MODE_KEYBOARD_MOUSE1) ? PRESET_MOUSE_LEFT_ANALOG - : (dbp_port_mode[port] == MODE_KEYBOARD_MOUSE2) ? PRESET_MOUSE_RIGHT_ANALOG + bool preset_mode = (mode >= MODE_PRESET_AUTOMAPPED && mode <= MODE_PRESET_LAST), bind_osd = (mode != MODE_DISABLED); + EPreset preset = (preset_mode) ? (EPreset)(PRESET_AUTOMAPPED + (mode - MODE_PRESET_AUTOMAPPED)) + : (mode == MODE_KEYBOARD_MOUSE1) ? PRESET_MOUSE_LEFT_ANALOG + : (mode == MODE_KEYBOARD_MOUSE2) ? PRESET_MOUSE_RIGHT_ANALOG : PRESET_NONE; if (bind_osd) Apply(port, PresetBinds(preset, port), true); if (preset_mode) FillGenericKeys(port); @@ -1342,7 +1359,7 @@ struct DBP_PadMapping input_names.clear(); std::vector input_descriptor; for (DBP_InputBind *b = (dbp_input_binds.empty() ? NULL : &dbp_input_binds[0]), *bEnd = b + dbp_input_binds.size(), *prev = NULL; b != bEnd; prev = b++) - if (b->device != RETRO_DEVICE_MOUSE && (!prev || PORT_DEVICE_INDEX_ID(*prev) != PORT_DEVICE_INDEX_ID(*b))) + if (b->device != RETRO_DEVICE_MOUSE && b->port < DBP_MAX_PORTS && (!prev || PORT_DEVICE_INDEX_ID(*prev) != PORT_DEVICE_INDEX_ID(*b))) if (const char* desc = GenerateDesc(input_names, PORT_DEVICE_INDEX_ID(*b), b->device == RETRO_DEVICE_ANALOG)) input_descriptor.push_back( { b->port, b->device, b->index, b->id, desc } ); input_descriptor.push_back( { 0 } ); @@ -1943,7 +1960,7 @@ static std::vector& DBP_ScanSystem(bool force_midi_scan) for (const std::string& s : dbp_shellzips) { fwrite(s.c_str(), s.length(), 1, f); fwrite("\n", 1, 1, f); } fclose(f); } - if (force_midi_scan) DBP_QueueEvent(DBPET_REFRESHSYSTEM); + if (force_midi_scan) DBP_QueueEvent(DBPET_REFRESHSYSTEM, DBP_NO_PORT); } return dynstr; } @@ -2057,7 +2074,7 @@ void GFX_EndUpdate(const Bit16u *changedLines) } } - if (dbp_intercept_next) + if (dbp_intercept_next && dbp_intercept_next->usegfx()) { if (dbp_opengl_draw && voodoo_ogl_is_showing()) // zero all including alpha because we'll blend the OSD after displaying voodoo memset(buf.video, 0, buf.width * buf.height * 4); @@ -2292,7 +2309,7 @@ void GFX_Events() { // Some configuration modifications (like keyboard layout) can cause this to be called recursively static bool GFX_EVENTS_RECURSIVE; - if (GFX_EVENTS_RECURSIVE) return; + if (GFX_EVENTS_RECURSIVE) { DBP_ASSERT(false); return; } // it probably isn't recursive anymore since config variable changing was moved to the main thread GFX_EVENTS_RECURSIVE = true; DBP_FPSCOUNT(dbp_fpscount_event) @@ -2320,16 +2337,19 @@ void GFX_Events() switch (e.type) { case DBPET_KEYDOWN: - BIOS_SetKeyboardLEDOverwrite((KBD_KEYS)e.val, (KBD_LEDS)e.val2); + if (e.port == DBP_KEYBOARD_PORT) BIOS_SetKeyboardLEDOverwrite((KBD_KEYS)e.val, (KBD_LEDS)e.val2); KEYBOARD_AddKey((KBD_KEYS)e.val, true); break; - case DBPET_KEYUP: KEYBOARD_AddKey((KBD_KEYS)e.val, false); break; + case DBPET_KEYUP: KEYBOARD_AddKey((KBD_KEYS)e.val, false); break; case DBPET_ONSCREENKEYBOARD: DBP_StartOSD(DBPOSD_OSK); break; case DBPET_ONSCREENKEYBOARDUP: break; - case DBPET_ACTIONWHEEL: DBP_WheelOSD((Bit8u)e.val2); break; - case DBPET_ACTIONWHEELUP: break; + case DBPET_ACTIONWHEEL: DBP_WheelShiftOSD(e.port, true); break; + case DBPET_ACTIONWHEELUP: DBP_WheelShiftOSD(e.port, false); break; + + case DBPET_SHIFTPORT: DBP_WheelShiftOSD(e.port, true, (Bit8u)e.val); break; + case DBPET_SHIFTPORTUP: DBP_WheelShiftOSD(e.port, false, (Bit8u)e.val); break; case DBPET_MOUSEMOVE: { @@ -3306,12 +3326,12 @@ void retro_init(void) //#3 { int leds = ((key_modifiers & RETROKMOD_NUMLOCK) ? KLED_NUMLOCK : 0) | ((key_modifiers & RETROKMOD_CAPSLOCK) ? KLED_CAPSLOCK : 0) | ((key_modifiers & RETROKMOD_SCROLLOCK) ? KLED_SCROLLLOCK : 0); dbp_keys_down[val] |= DBP_DOWN_BY_KEYBOARD; - DBP_QueueEvent(DBPET_KEYDOWN, val, leds); + DBP_QueueEvent(DBPET_KEYDOWN, DBP_KEYBOARD_PORT, val, leds); } else if (!down && (dbp_keys_down[val] & DBP_DOWN_BY_KEYBOARD)) { dbp_keys_down[val] = 1; - DBP_QueueEvent(DBPET_KEYUP, val); + DBP_QueueEvent(DBPET_KEYUP, DBP_KEYBOARD_PORT, val); } } @@ -3326,7 +3346,7 @@ void retro_init(void) //#3 DBP_Mount(dbp_image_index, true); DBP_SetMountSwappingRequested(); // set swapping_requested flag for CMscdex::GetMediaStatus DBP_ThreadControl(TCM_RESUME_FRAME); - DBP_QueueEvent(DBPET_CHANGEMOUNTS); + DBP_QueueEvent(DBPET_CHANGEMOUNTS, DBP_NO_PORT); return true; } @@ -3748,9 +3768,9 @@ void retro_run_touchpad(bool has_press, Bit16s absx, Bit16s absy) if (add_press) press_tick = tick; if (!down_tick && !add_press && press_tick && (!is_move || presses)) - { down_tick = tick; is_tap = true; down_btn = presses; DBP_QueueEvent(DBPET_MOUSEDOWN, down_btn); press_tick = 0; } + { down_tick = tick; is_tap = true; down_btn = presses; DBP_QueueEvent(DBPET_MOUSEDOWN, DBP_NO_PORT, down_btn); press_tick = 0; } else if (down_tick && (!presses || add_press)) - { DBP_QueueEvent(DBPET_MOUSEUP, down_btn); down_tick = 0; } + { DBP_QueueEvent(DBPET_MOUSEUP, DBP_NO_PORT, down_btn); down_tick = 0; } if (!presses) is_move = false; if (!last_presses || !add_press) @@ -3764,14 +3784,14 @@ void retro_run_touchpad(bool has_press, Bit16s absx, Bit16s absy) { lastx = absx; int tx = dx + dbp_mouse_x; dbp_mouse_x = (Bit16s)(tx < -32768 ? -32768 : (tx > 32767 ? 32767 : tx)); dx += remx; remx = (Bit16s)(dx % 32); lasty = absy; int ty = dy + dbp_mouse_y; dbp_mouse_y = (Bit16s)(ty < -32768 ? -32768 : (ty > 32767 ? 32767 : ty)); dy += remy; remy = (Bit16s)(dy % 32); - DBP_QueueEvent(DBPET_MOUSEMOVE, dx / 32, dy / 32); + DBP_QueueEvent(DBPET_MOUSEMOVE, DBP_NO_PORT, dx / 32, dy / 32); is_move = true; } } if (!down_tick && presses && !is_move && press_tick && (tick - press_tick) >= 500) - { down_tick = tick; is_tap = false; down_btn = presses - 1; DBP_QueueEvent(DBPET_MOUSEDOWN, down_btn); } + { down_tick = tick; is_tap = false; down_btn = presses - 1; DBP_QueueEvent(DBPET_MOUSEDOWN, DBP_NO_PORT, down_btn); } else if (down_tick && is_tap && (tick - down_tick) >= 100) - { DBP_QueueEvent(DBPET_MOUSEUP, down_btn); down_tick = 0; } + { DBP_QueueEvent(DBPET_MOUSEUP, DBP_NO_PORT, down_btn); down_tick = 0; } } void retro_run(void) @@ -3882,7 +3902,7 @@ void retro_run(void) { //log_cb(RETRO_LOG_INFO, "[DOSBOX MOUSE] [%4d@%6d] Rel: %d,%d - Abs: %d,%d - LG: %d,%d - Press: %d - Count: %d\n", dbp_framecount, DBP_GetTicks(), movx, movy, absx, absy, lgx, lgy, prss, input_state_cb(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_COUNT)); if (absvalid) dbp_mouse_x = absx, dbp_mouse_y = absy; - DBP_QueueEvent(DBPET_MOUSEMOVE, movx, movy); + DBP_QueueEvent(DBPET_MOUSEMOVE, DBP_NO_PORT, movx, movy); } } @@ -3897,7 +3917,7 @@ void retro_run(void) if (b.evt > _DBPET_JOY_AXIS_MAX || b.device != RETRO_DEVICE_JOYPAD) continue; // handled below Bit16s val = input_state_cb(b.port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_BUTTON, b.id); if (!val) val = (input_state_cb(b.port, RETRO_DEVICE_JOYPAD, 0, b.id) ? (Bit16s)32767 : (Bit16s)0); // old frontend fallback - if (val != b.lastval) b.Update(val, true); + if (val != b.lastval) b.Update(val); } } diff --git a/dosbox_pure_osd.h b/dosbox_pure_osd.h index 369f89b5..effceb1d 100644 --- a/dosbox_pure_osd.h +++ b/dosbox_pure_osd.h @@ -503,7 +503,7 @@ struct DBP_MapperMenuState : DBP_MenuState else if (b.evt == DBPET_AXISMAPPAIR) key = DBP_MAPPAIR_GET(apart?1:-1, b.meta); else for (const DBP_SpecialMapping& sm : DBP_SpecialMappings) - if (sm.evt == b.evt && (!sm.dev || sm.meta == (b.device == RETRO_DEVICE_ANALOG ? (apart ? 1 : -1) : b.meta))) + if (sm.evt == b.evt && sm.meta == (b.device == RETRO_DEVICE_ANALOG ? (apart ? 1 : -1) : b.meta)) { key = DBP_SPECIALMAPPINGS_KEY + (int)(&sm - DBP_SpecialMappings); break; } if (key < 0) { DBP_ASSERT(false); return; } @@ -520,12 +520,13 @@ struct DBP_MapperMenuState : DBP_MenuState { int maxport = 1; while (maxport != DBP_MAX_PORTS && dbp_port_mode[maxport]) maxport++; + for (DBP_InputBind& b : dbp_input_binds) if (b.evt == DBPET_SHIFTPORT && b.meta >= maxport) maxport = b.meta + 1; bind_port = (bind_port + maxport + x_change) % maxport; main_sel = 0; } list.clear(); - if (dbp_port_mode[bind_port] != DBP_PadMapping::MODE_MAPPER) + if (DBP_PadMapping::CalcPortMode(bind_port) != DBP_PadMapping::MODE_MAPPER) { list.emplace_back(IT_NONE); list.emplace_back(IT_NONE, 11, " Gamepad Mapper is disabled"); @@ -661,9 +662,13 @@ struct DBP_MapperMenuState : DBP_MenuState list.emplace_back(IT_DEVICE, 1, " "); list.back().str += DBPDEV_Keyboard; list.emplace_back(IT_DEVICE, 2, " "); list.back().str += DBPDEV_Mouse; list.emplace_back(IT_DEVICE, 3, " "); list.back().str += DBPDEV_Joystick; - list.emplace_back(IT_SELECT, DBP_SPECIALMAPPINGS_OSK, " "); list.back().str += DBP_SPECIALMAPPING(DBP_SPECIALMAPPINGS_OSK).name; - if (edit_info >= 0) // editing bind - { list.emplace_back(IT_SELECT, DBP_SPECIALMAPPINGS_ACTIONWHEEL, " "); list.back().str += DBP_SPECIALMAPPING(DBP_SPECIALMAPPINGS_ACTIONWHEEL).name; } + for (const DBP_SpecialMapping& sm : DBP_SpecialMappings) + { + if (sm.dev || (sm.evt != DBPET_ONSCREENKEYBOARD && edit_info < 0)) continue; // no wheel no port shift as wheel options + if (sm.evt == DBPET_SHIFTPORT && sm.meta == bind_port) continue; + list.emplace_back(IT_SELECT, (Bit16s)(DBP_SPECIALMAPPINGS_KEY + (&sm - DBP_SpecialMappings)), " "); + list.back().str += sm.name; + } if (edit_mode == EDIT_EXISTING) { list.emplace_back(IT_NONE); @@ -818,7 +823,7 @@ struct DBP_MapperMenuState : DBP_MenuState void DrawMenu(DBP_BufferDrawing& buf, Bit32u blend, int lh, int w, int h, int ftr, bool mouseMoved, const DBP_MenuMouse& m) { UpdateHeld(); - if ((dbp_port_mode[bind_port] == DBP_PadMapping::MODE_MAPPER) == is_mapper_disabled_top()) + if ((DBP_PadMapping::CalcPortMode(bind_port) == DBP_PadMapping::MODE_MAPPER) == is_mapper_disabled_top()) menu_top(); int hdr = lh*3, rows = (h - hdr - ftr) / lh-1, count = (int)list.size(), l = w/2 - 150, r = w/2 + 150, xtra = (lh == 8 ? 0 : 1), wide = edit_mode == NOT_EDITING && !is_presets_menu() && w > 500; @@ -1828,14 +1833,15 @@ static void DBP_PureMenuProgram(Program** make) *make = new Menu; } -static void DBP_WheelOSD(Bit8u _port) +static void DBP_WheelShiftOSD(Bit8u _port, bool _on, Bit8u _shift_port = (Bit8u)-1) { struct Wheel { - bool SetOpen, UseMouse, ButtonDown; - enum EState : Bit8u { STATE_CLOSED, STATE_OPEN, STATE_OPEN_PRESSED, STATE_CLOSING_PRESSED, STATE_CLOSING_RELEASED } State; - Bit16s PosX, PosY; - int Result; + enum EFlags : Bit8u { FLAG_OPENWHEEL = 1, FLAG_CLOSEWHEEL = 2, FLAG_SETSHIFT = 4, FLAG_CLEARSHIFT = 8 }; + enum EState : Bit8u { STATE_CLOSED, STATE_OPEN, STATE_OPEN_PRESSED, STATE_CLOSING_PRESSED, STATE_CLOSING_RELEASED }; + Bit8u State, Flags, Shift; + bool ButtonDown, UseMouse; + Bit16s PosX, PosY, Result; Bit32u Tick; static bool InAngleRange(float a, float b, float halfstep) @@ -1846,8 +1852,15 @@ static void DBP_WheelOSD(Bit8u _port) return (off > -halfstep && off < halfstep); } - void Draw(int port, DBP_BufferDrawing& drw) + void Draw(Bit8u port, DBP_BufferDrawing& drw) { + Bit8u wheelport = (Shift ? Shift - 1 : port); + int n = 0; + for (const DBP_WheelItem& wi : dbp_wheelitems) + if (wi.port == wheelport) + n++; + if (!n) return; + int alpha = 0; Bit32u tickd = (DBP_GetTicks() - Tick); if (State == STATE_OPEN) alpha = tickd*2; @@ -1856,12 +1869,6 @@ static void DBP_WheelOSD(Bit8u _port) if (alpha <= 0) return; if (alpha >= 0xFF) alpha = 0xFF; - int n = 0; - for (const DBP_WheelItem& wi : dbp_wheelitems) - if (wi.port == port) - n++; - if (!n) return; - int cx = drw.width * (port ? 2 : 5) / 8, cy = drw.height * 2 / 3, rad = (drw.height > 900 ? 150 : (drw.height / 6)), rad4 = rad + 4, radtxt = rad + 7; int srad = rad / 9, maxdist = (rad-srad+3), maxdistsq = maxdist * maxdist, seldist = rad / 2, seldistsq = seldist * seldist; @@ -1875,9 +1882,9 @@ static void DBP_WheelOSD(Bit8u _port) if (State == STATE_OPEN) Result = -1; for (const DBP_WheelItem& wi : dbp_wheelitems) { - if (wi.port != port) continue; - if (sela == -999.f && (State == STATE_OPEN ? InAngleRange(a, wa, astephalf) : Result == (int)(&wi - &dbp_wheelitems[0]))) sela = a; - if (sela == a && wdistsq > seldistsq && State == STATE_OPEN) Result = (int)(&wi - &dbp_wheelitems[0]); + if (wi.port != wheelport) continue; + if (sela == -999.f && (State == STATE_OPEN ? InAngleRange(a, wa, astephalf) : Result == (Bit16s)(&wi - &dbp_wheelitems[0]))) sela = a; + if (sela == a && wdistsq > seldistsq && State == STATE_OPEN) Result = (Bit16s)(&wi - &dbp_wheelitems[0]); a += astep; } @@ -1914,7 +1921,7 @@ static void DBP_WheelOSD(Bit8u _port) int selnx = 0, selny = 0, lh = (drw.height >= 400 ? 14 : 8); const char* sellbl = NULL; for (const DBP_WheelItem& wi : dbp_wheelitems) { - if (wi.port != port) continue; + if (wi.port != wheelport) continue; const char* lbl = DBP_PadMapping::GetWheelAutoMapButtonLabel(wi); if (!lbl) lbl = DBP_GETKEYNAME(wi.k[wi.key_count - 1]); @@ -1932,9 +1939,10 @@ static void DBP_WheelOSD(Bit8u _port) if (sellbl) drw.PrintOutlined(lh, cx + selnx, cy + selny - 3, sellbl, selcolor, black); } - void Update(int port) + void Update(Bit8u port) { - if (SetOpen) { SetState(port, STATE_OPEN); SetOpen = false; } + if (Flags & FLAG_OPENWHEEL) { SetState(port, STATE_OPEN); Flags &= ~FLAG_OPENWHEEL; } + if (Flags & FLAG_CLOSEWHEEL) { SetState(port, STATE_CLOSING_PRESSED); Flags &= ~FLAG_CLOSEWHEEL; } if (State == STATE_CLOSING_PRESSED && (DBP_GetTicks() - Tick) > 70) SetState(port, STATE_CLOSING_RELEASED); if (State == STATE_CLOSING_RELEASED && (DBP_GetTicks() - Tick) > 250) SetState(port, STATE_CLOSED); if (State != STATE_OPEN && State != STATE_OPEN_PRESSED) return; @@ -1981,11 +1989,11 @@ static void DBP_WheelOSD(Bit8u _port) } } - void SetState(int port, EState newstate) + void SetState(Bit8u port, EState newstate) { - //static const char *statename[] = { "CLOSED", "OPEN", "CLOSING_PRESSED", "CLOSING_RELEASED" }; + //static const char *statename[] = { "CLOSED", "OPEN", "OPEN_PRESSED", "CLOSING_PRESSED", "CLOSING_RELEASED" }; //log_cb(RETRO_LOG_INFO, "[WHEEL%d] Setting state from %s to %s (current result: %d)\n", port, statename[(int)State], statename[(int)newstate], Result); - if (newstate == State) return; + if (newstate == State || (newstate == STATE_CLOSING_PRESSED && (State == STATE_CLOSED || State == STATE_CLOSING_RELEASED))) return; if (newstate == STATE_OPEN && State == STATE_CLOSED) { @@ -2014,57 +2022,107 @@ static void DBP_WheelOSD(Bit8u _port) State = newstate; } + + void ApplyShift(Bit8u port) + { + if (!(Flags & Wheel::FLAG_SETSHIFT)) { Shift = 0; Flags &= ~Wheel::FLAG_CLEARSHIFT; ClearShifts(); return; } + Flags &= ~Wheel::FLAG_SETSHIFT; + Bit8u shift_port = Shift - 1; + for (size_t i = 0, iEnd = dbp_input_binds.size(); i != iEnd; i++) + { + DBP_InputBind b = dbp_input_binds[i]; + if (b.port != shift_port || b.device == RETRO_DEVICE_MOUSE || b.evt == DBPET_SHIFTPORT) continue; + b.port = port; // for PORT_DEVICE_INDEX_ID + for (DBP_InputBind &b2 : dbp_input_binds) + if (PORT_DEVICE_INDEX_ID(b) == PORT_DEVICE_INDEX_ID(b2) && b2.evt == DBPET_SHIFTPORT) + goto skip_bind; + b.port = port | DBP_SHIFT_PORT_BIT; + b.lastval = 0; + dbp_input_binds.push_back(b); + skip_bind:; + } + } + + static void ClearShifts() + { + for (size_t i = dbp_input_binds.size(); i--;) + { + DBP_InputBind& b = dbp_input_binds[i]; + if (!(b.port & DBP_SHIFT_PORT_BIT) || Wheels[b.port & DBP_PORT_MASK].Shift) continue; + if (b.lastval) b.Update(0); + dbp_input_binds.erase(dbp_input_binds.begin() + i); + } + } }; static Wheel Wheels[DBP_MAX_PORTS]; static struct WheelInterceptor : DBP_Interceptor { + virtual bool usegfx() + { + for (Bit8u port = 0; port != DBP_MAX_PORTS; port++) if (Wheels[port].State) return true; + for (Bit8u port = 0; port != DBP_MAX_PORTS; port++) if (Wheels[port].Shift || Wheels[port].Flags) return false; + DBP_SetIntercept(NULL); + return false; + } + virtual void gfx(DBP_Buffer& buf) override { - int openwheels = 0; - for (int port = 0; port != DBP_MAX_PORTS; port++) - if (Wheels[port].SetOpen || Wheels[port].State) - { Wheels[port].Draw(port, static_cast(buf)); openwheels++; } - if (!openwheels) - DBP_SetIntercept(NULL); + for (Bit8u port = 0; port != DBP_MAX_PORTS; port++) if (Wheels[port].State) Wheels[port].Draw(port, static_cast(buf)); } virtual void input() override { - for (int port = 0; port != DBP_MAX_PORTS; port++) - if (Wheels[port].SetOpen || Wheels[port].State) - Wheels[port].Update(port); + for (Bit8u port = 0; port != DBP_MAX_PORTS; port++) + { + Wheel& wheel = Wheels[port]; + if ((wheel.Flags & (Wheel::FLAG_OPENWHEEL | Wheel::FLAG_CLOSEWHEEL)) || wheel.State) wheel.Update(port); + if (wheel.Flags & (Wheel::FLAG_SETSHIFT | Wheel::FLAG_CLEARSHIFT)) wheel.ApplyShift(port); + } - const Bit8u aw = dbp_actionwheel_inputs; - for (DBP_InputBind &b : dbp_input_binds) + for (DBP_InputBind& b : dbp_input_binds) { - Bit16s val = input_state_cb(b.port, b.device, b.index, b.id); - bool is_analog_button = (dbp_analog_buttons && b.device == RETRO_DEVICE_JOYPAD && b.evt <= _DBPET_JOY_AXIS_MAX); - if (is_analog_button) + Bit8u port = (b.port & DBP_PORT_MASK); + Wheel& wheel = Wheels[port]; + Bit16s val = 0; + if (!wheel.Shift || (b.port & DBP_SHIFT_PORT_BIT) || b.device == RETRO_DEVICE_MOUSE || b.evt == DBPET_SHIFTPORT) { - Bit16s aval = input_state_cb(b.port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_BUTTON, b.id); - if (aval) val = aval; else val = (val ? (Bit16s)32767 : (Bit16s)0); // old frontend fallback - } - - if ((Wheels[b.port].State == Wheel::STATE_OPEN || Wheels[b.port].State == Wheel::STATE_OPEN_PRESSED || Wheels[b.port].State == Wheel::STATE_CLOSING_PRESSED) && b.evt != DBPET_ACTIONWHEEL && ( - (b.device == RETRO_DEVICE_ANALOG && ((b.index == RETRO_DEVICE_INDEX_ANALOG_LEFT && (aw&1)) || (b.index == RETRO_DEVICE_INDEX_ANALOG_RIGHT && (aw&2)))) || - (b.device == RETRO_DEVICE_JOYPAD && (b.id == RETRO_DEVICE_ID_JOYPAD_B || ((b.id == RETRO_DEVICE_ID_JOYPAD_UP || b.id == RETRO_DEVICE_ID_JOYPAD_DOWN || b.id == RETRO_DEVICE_ID_JOYPAD_LEFT || b.id == RETRO_DEVICE_ID_JOYPAD_RIGHT) && (aw&4)))) || - (b.device == RETRO_DEVICE_MOUSE && (aw&8)))) - val = 0; + val = input_state_cb(port, b.device, b.index, b.id); + if (dbp_analog_buttons && b.device == RETRO_DEVICE_JOYPAD && b.evt <= _DBPET_JOY_AXIS_MAX) + { + Bit16s aval = input_state_cb(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_BUTTON, b.id); + if (aval) val = aval; else val = (val ? (Bit16s)32767 : (Bit16s)0); // old frontend fallback + } - if (val == b.lastval) continue; - b.Update(val, is_analog_button); - if (b.evt == DBPET_ACTIONWHEEL) Wheels[b.port].SetState(b.port, (val ? Wheel::STATE_OPEN : Wheel::STATE_CLOSING_PRESSED)); + if (val && (wheel.State == Wheel::STATE_OPEN || wheel.State == Wheel::STATE_OPEN_PRESSED || wheel.State == Wheel::STATE_CLOSING_PRESSED) && b.evt != DBPET_ACTIONWHEEL && ( + (b.device == RETRO_DEVICE_ANALOG && ((b.index == RETRO_DEVICE_INDEX_ANALOG_LEFT && (dbp_actionwheel_inputs&1)) || (b.index == RETRO_DEVICE_INDEX_ANALOG_RIGHT && (dbp_actionwheel_inputs&2)))) || + (b.device == RETRO_DEVICE_JOYPAD && (b.id == RETRO_DEVICE_ID_JOYPAD_B || ((b.id == RETRO_DEVICE_ID_JOYPAD_UP || b.id == RETRO_DEVICE_ID_JOYPAD_DOWN || b.id == RETRO_DEVICE_ID_JOYPAD_LEFT || b.id == RETRO_DEVICE_ID_JOYPAD_RIGHT) && (dbp_actionwheel_inputs&4)))) || + (b.device == RETRO_DEVICE_MOUSE && (dbp_actionwheel_inputs&8)))) + val = 0; + } + if (val != b.lastval) b.Update(val); } } virtual void close() override { - for (int port = 0; port != DBP_MAX_PORTS; port++) - if (Wheels[port].State) - Wheels[port].SetState(port, Wheel::STATE_CLOSED); + for (Bit8u port = 0; port != DBP_MAX_PORTS; port++) + { + Wheels[port].SetState(port, Wheel::STATE_CLOSED); + Wheels[port].Shift = Wheels[port].Flags = 0; + } + Wheel::ClearShifts(); } } interceptor; - Wheels[_port].SetOpen = true; + + if (!_on && dbp_intercept_next != &interceptor) return; + Wheel& wheel = Wheels[_port & DBP_PORT_MASK]; + if (_shift_port == (Bit8u)-1) + wheel.Flags = ((wheel.Flags & ~(Wheel::FLAG_OPENWHEEL & Wheel::FLAG_CLOSEWHEEL)) | (_on ? Wheel::FLAG_OPENWHEEL : Wheel::FLAG_CLOSEWHEEL)); + else + { + wheel.Flags = ((wheel.Flags & ~(Wheel::FLAG_SETSHIFT & Wheel::FLAG_CLEARSHIFT)) | (_on ? Wheel::FLAG_SETSHIFT : Wheel::FLAG_CLEARSHIFT)); + if (_on) wheel.Shift = _shift_port + 1; + } DBP_SetIntercept(&interceptor); }