diff options
Diffstat (limited to 'src/osd')
101 files changed, 5945 insertions, 3041 deletions
diff --git a/src/osd/asio.h b/src/osd/asio.h index d59a90b8929..0643bf332b4 100644 --- a/src/osd/asio.h +++ b/src/osd/asio.h @@ -18,18 +18,12 @@ #endif #if defined(_WIN32) && !defined(_WIN32_WINNT) -#if defined(OSD_WINDOWS) -#define _WIN32_WINNT 0x0501 -#else -#define _WIN32_WINNT 0x0603 -#endif +#define _WIN32_WINNT 0x0600 #endif + #define ASIO_HEADER_ONLY #define ASIO_STANDALONE #define ASIO_SEPARATE_COMPILATION -#define ASIO_NOEXCEPT noexcept(true) -#define ASIO_NOEXCEPT_OR_NOTHROW noexcept(true) -#define ASIO_ERROR_CATEGORY_NOEXCEPT noexcept(true) #include <asio.hpp> #undef interface diff --git a/src/osd/eivc.h b/src/osd/eivc.h index d17fbc2d0c4..5c41726d2b1 100644 --- a/src/osd/eivc.h +++ b/src/osd/eivc.h @@ -15,8 +15,10 @@ #include <intrin.h> #include <stdlib.h> + #pragma intrinsic(_BitScanReverse) -#ifdef PTR64 + +#if defined(_M_X64) || defined(_M_ARM64) #pragma intrinsic(_BitScanReverse64) #endif @@ -65,7 +67,7 @@ __forceinline uint8_t _count_leading_ones_32(uint32_t value) __forceinline uint8_t _count_leading_zeros_64(uint64_t value) { unsigned long index; -#ifdef PTR64 +#if defined(_M_X64) || defined(_M_ARM64) return _BitScanReverse64(&index, value) ? (63U - index) : 64U; #else return _BitScanReverse(&index, uint32_t(value >> 32)) ? (31U - index) : _BitScanReverse(&index, uint32_t(value)) ? (63U - index) : 64U; @@ -84,7 +86,7 @@ __forceinline uint8_t _count_leading_zeros_64(uint64_t value) __forceinline uint8_t _count_leading_ones_64(uint64_t value) { unsigned long index; -#ifdef PTR64 +#if defined(_M_X64) || defined(_M_ARM64) return _BitScanReverse64(&index, ~value) ? (63U - index) : 64U; #else return _BitScanReverse(&index, ~uint32_t(value >> 32)) ? (31U - index) : _BitScanReverse(&index, ~uint32_t(value)) ? (63U - index) : 64U; diff --git a/src/osd/eivcx86.h b/src/osd/eivcx86.h index eb8811ad8bc..f6c4c90d9a2 100644 --- a/src/osd/eivcx86.h +++ b/src/osd/eivcx86.h @@ -13,7 +13,7 @@ #pragma once -#ifdef PTR64 +#if defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2)) #include <emmintrin.h> #endif @@ -29,7 +29,7 @@ multiply and return the full 64 bit result -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mul_32x32 _mul_32x32 inline int64_t _mul_32x32(int32_t a, int32_t b) { @@ -50,7 +50,7 @@ inline int64_t _mul_32x32(int32_t a, int32_t b) result -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mulu_32x32 _mulu_32x32 inline uint64_t _mulu_32x32(uint32_t a, uint32_t b) { @@ -71,7 +71,7 @@ inline uint64_t _mulu_32x32(uint32_t a, uint32_t b) result -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mul_32x32_hi _mul_32x32_hi inline int32_t _mul_32x32_hi(int32_t a, int32_t b) { @@ -95,7 +95,7 @@ inline int32_t _mul_32x32_hi(int32_t a, int32_t b) of the result -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mulu_32x32_hi _mulu_32x32_hi inline uint32_t _mulu_32x32_hi(uint32_t a, uint32_t b) { @@ -120,7 +120,7 @@ inline uint32_t _mulu_32x32_hi(uint32_t a, uint32_t b) result to 32 bits -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mul_32x32_shift _mul_32x32_shift static inline int32_t _mul_32x32_shift(int32_t a, int32_t b, uint8_t shift) { @@ -147,7 +147,7 @@ static inline int32_t _mul_32x32_shift(int32_t a, int32_t b, uint8_t shift) result to 32 bits -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mulu_32x32_shift _mulu_32x32_shift inline uint32_t _mulu_32x32_shift(uint32_t a, uint32_t b, uint8_t shift) { @@ -172,7 +172,7 @@ inline uint32_t _mulu_32x32_shift(uint32_t a, uint32_t b, uint8_t shift) divide and return the 32 bit quotient -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define div_64x32 _div_64x32 inline int32_t _div_64x32(int64_t a, int32_t b) { @@ -198,7 +198,7 @@ inline int32_t _div_64x32(int64_t a, int32_t b) divide and return the 32 bit quotient -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define divu_64x32 _divu_64x32 inline uint32_t _divu_64x32(uint64_t a, uint32_t b) { @@ -225,7 +225,7 @@ inline uint32_t _divu_64x32(uint64_t a, uint32_t b) 32 bit remainder -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define div_64x32_rem _div_64x32_rem inline int32_t _div_64x32_rem(int64_t a, int32_t b, int32_t &remainder) { @@ -255,7 +255,7 @@ inline int32_t _div_64x32_rem(int64_t a, int32_t b, int32_t &remainder) and 32 bit remainder -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define divu_64x32_rem _divu_64x32_rem inline uint32_t _divu_64x32_rem(uint64_t a, uint32_t b, uint32_t &remainder) { @@ -285,7 +285,7 @@ inline uint32_t _divu_64x32_rem(uint64_t a, uint32_t b, uint32_t &remainder) division, and returning the 32 bit quotient -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define div_32x32_shift _div_32x32_shift inline int32_t _div_32x32_shift(int32_t a, int32_t b, uint8_t shift) { @@ -313,7 +313,7 @@ inline int32_t _div_32x32_shift(int32_t a, int32_t b, uint8_t shift) division, and returning the 32 bit quotient -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define divu_32x32_shift _divu_32x32_shift inline uint32_t _divu_32x32_shift(uint32_t a, uint32_t b, uint8_t shift) { @@ -340,7 +340,7 @@ inline uint32_t _divu_32x32_shift(uint32_t a, uint32_t b, uint8_t shift) divide and return the 32 bit remainder -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define mod_64x32 _mod_64x32 static inline int32_t _mod_64x32(int64_t a, int32_t b) { @@ -366,7 +366,7 @@ static inline int32_t _mod_64x32(int64_t a, int32_t b) divide and return the 32 bit remainder -------------------------------------------------*/ -#ifndef PTR64 +#ifndef _M_X64 #define modu_64x32 _modu_64x32 inline uint32_t _modu_64x32(uint64_t a, uint32_t b) { @@ -392,7 +392,7 @@ inline uint32_t _modu_64x32(uint64_t a, uint32_t b) point reciprocal -------------------------------------------------*/ -#ifdef PTR64 +#if defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 2)) #define recip_approx _recip_approx inline float _recip_approx(float z) { @@ -410,7 +410,7 @@ inline float _recip_approx(float z) multiply and return the full 128 bit result -------------------------------------------------*/ -#ifdef PTR64 +#ifdef _M_X64 #define mul_64x64 _mul_64x64 __forceinline int64_t _mul_64x64(int64_t a, int64_t b, int64_t &hi) { @@ -424,7 +424,7 @@ __forceinline int64_t _mul_64x64(int64_t a, int64_t b, int64_t &hi) bit multiply and return the full 128 bit result -------------------------------------------------*/ -#ifdef PTR64 +#ifdef _M_X64 #define mulu_64x64 _mulu_64x64 __forceinline int64_t _mulu_64x64(uint64_t a, uint64_t b, uint64_t &hi) { @@ -455,7 +455,7 @@ __forceinline bool _addu_32x32_co(uint32_t a, uint32_t b, uint32_t &sum) #define addu_64x64_co _addu_64x64_co __forceinline bool _addu_64x64_co(uint64_t a, uint64_t b, uint64_t &sum) { -#ifdef PTR64 +#ifdef _M_X64 return _addcarry_u64(0, a, b, &sum); #else uint32_t l, h; diff --git a/src/osd/interface/audio.h b/src/osd/interface/audio.h new file mode 100644 index 00000000000..f40744cfff6 --- /dev/null +++ b/src/osd/interface/audio.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#ifndef MAME_OSD_INTERFACE_AUDIO_H +#define MAME_OSD_INTERFACE_AUDIO_H + +#pragma once + +#include <array> +#include <cmath> +#include <string> +#include <vector> + + +namespace osd { + +struct audio_rate_range { + uint32_t m_default_rate; + uint32_t m_min_rate; + uint32_t m_max_rate; +}; + +struct audio_info { + struct node_info { + std::string m_name; + uint32_t m_id; + audio_rate_range m_rate; + std::vector<std::string> m_port_names; + std::vector<std::array<double, 3> > m_port_positions; + uint32_t m_sinks; + uint32_t m_sources; + + std::string name() const { return (m_sinks ? "o:" : "i:") + m_name; } + }; + + struct stream_info { + uint32_t m_id; + uint32_t m_node; + std::vector<float> m_volumes; + }; + + uint32_t m_generation; + uint32_t m_default_sink; + uint32_t m_default_source; + std::vector<node_info> m_nodes; + std::vector<stream_info> m_streams; +}; + +inline float db_to_linear(float db) { return (db <= -96.0F) ? 0.0F : std::pow(10.0F, db / 20.0F); } +inline float linear_to_db(float linear) { return (linear <= (1.0F / 65536.0F)) ? -96.0F : (20.0F * std::log10(linear)); } + +} // namespace osd + +#endif // MAME_OSD_INTERFACE_AUDIO_H diff --git a/src/osd/interface/inputcode.h b/src/osd/interface/inputcode.h index 69ae1b13e33..c056e78f401 100644 --- a/src/osd/interface/inputcode.h +++ b/src/osd/interface/inputcode.h @@ -379,7 +379,7 @@ public: // setters void set_device_class(input_device_class devclass) noexcept { - assert(devclass >= 0 && devclass <= 0xf); + assert(0 == (std::underlying_type_t<input_device_class>(devclass) & ~0xf)); m_internal = (m_internal & ~(0xf << 28)) | ((devclass & 0xf) << 28); } void set_device_index(int devindex) noexcept @@ -389,17 +389,17 @@ public: } void set_item_class(input_item_class itemclass) noexcept { - assert(itemclass >= 0 && itemclass <= 0xf); + assert(0 == (std::underlying_type_t<input_item_class>(itemclass) & ~0xf)); m_internal = (m_internal & ~(0xf << 16)) | ((itemclass & 0xf) << 16); } void set_item_modifier(input_item_modifier modifier) noexcept { - assert(modifier >= 0 && modifier <= 0xf); + assert(0 == (std::underlying_type_t<input_item_modifier>(modifier) & ~0xf)); m_internal = (m_internal & ~(0xf << 12)) | ((modifier & 0xf) << 12); } void set_item_id(input_item_id itemid) noexcept { - assert(itemid >= 0 && itemid <= 0xfff); + assert(0 == (std::underlying_type_t<input_item_id>(itemid) & ~0xfff)); m_internal = (m_internal & ~0xfff) | (itemid & 0xfff); } @@ -888,19 +888,19 @@ constexpr input_code JOYCODE_HAT4RIGHT_INDEXED(int n) { return input_code(DEVICE #define JOYCODE_HAT1UP JOYCODE_HAT1UP_INDEXED(0) #define JOYCODE_HAT1DOWN JOYCODE_HAT1DOWN_INDEXED(0) -#define JOYCODE_HAT1LEFT JOYCODE_HAT1EFT_INDEXED(0) +#define JOYCODE_HAT1LEFT JOYCODE_HAT1LEFT_INDEXED(0) #define JOYCODE_HAT1RIGHT JOYCODE_HAT1RIGHT_INDEXED(0) #define JOYCODE_HAT2UP JOYCODE_HAT2UP_INDEXED(0) #define JOYCODE_HAT2DOWN JOYCODE_HAT2DOWN_INDEXED(0) -#define JOYCODE_HAT2LEFT JOYCODE_HAT2EFT_INDEXED(0) +#define JOYCODE_HAT2LEFT JOYCODE_HAT2LEFT_INDEXED(0) #define JOYCODE_HAT2RIGHT JOYCODE_HAT2RIGHT_INDEXED(0) #define JOYCODE_HAT3UP JOYCODE_HAT3UP_INDEXED(0) #define JOYCODE_HAT3DOWN JOYCODE_HAT3DOWN_INDEXED(0) -#define JOYCODE_HAT3LEFT JOYCODE_HAT3EFT_INDEXED(0) +#define JOYCODE_HAT3LEFT JOYCODE_HAT3LEFT_INDEXED(0) #define JOYCODE_HAT3RIGHT JOYCODE_HAT3RIGHT_INDEXED(0) #define JOYCODE_HAT4UP JOYCODE_HAT4UP_INDEXED(0) #define JOYCODE_HAT4DOWN JOYCODE_HAT4DOWN_INDEXED(0) -#define JOYCODE_HAT4LEFT JOYCODE_HAT4EFT_INDEXED(0) +#define JOYCODE_HAT4LEFT JOYCODE_HAT4LEFT_INDEXED(0) #define JOYCODE_HAT4RIGHT JOYCODE_HAT4RIGHT_INDEXED(0) diff --git a/src/osd/interface/midiport.h b/src/osd/interface/midiport.h new file mode 100644 index 00000000000..47c5bbc5c52 --- /dev/null +++ b/src/osd/interface/midiport.h @@ -0,0 +1,50 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + midiport.h + + OSD interface to midi ports + +***************************************************************************/ +#ifndef MAME_OSD_INTERFACE_MIDIPORT_H +#define MAME_OSD_INTERFACE_MIDIPORT_H + +#pragma once + +#include <cstdint> +#include <string> + + +namespace osd { + +class midi_input_port +{ +public: + virtual ~midi_input_port() = default; + + virtual bool poll() = 0; + virtual int read(uint8_t *pOut) = 0; +}; + + +class midi_output_port +{ +public: + virtual ~midi_output_port() = default; + + virtual void write(uint8_t data) = 0; +}; + +struct midi_port_info +{ + std::string name; + bool input; + bool output; + bool default_input; + bool default_output; +}; + +} // namespace osd + +#endif // MAME_OSD_INTERFACE_MIDIPORT_H diff --git a/src/osd/interface/nethandler.cpp b/src/osd/interface/nethandler.cpp new file mode 100644 index 00000000000..f3715df9b12 --- /dev/null +++ b/src/osd/interface/nethandler.cpp @@ -0,0 +1,23 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + nethandler.cpp + + OSD interface to virtual networking handlers + +***************************************************************************/ + +#include "nethandler.h" + +#include <algorithm> + + +namespace osd { + +network_handler::network_handler() noexcept +{ + std::fill(std::begin(m_mac), std::end(m_mac), 0); +} + +} // namespace osd diff --git a/src/osd/interface/nethandler.h b/src/osd/interface/nethandler.h new file mode 100644 index 00000000000..9513b158d99 --- /dev/null +++ b/src/osd/interface/nethandler.h @@ -0,0 +1,70 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + nethandler.h + + OSD interface to virtual networking handlers + +***************************************************************************/ +#ifndef MAME_OSD_INTERFACE_NETHANDLER_H +#define MAME_OSD_INTERFACE_NETHANDLER_H + +#pragma once + +#include "osdcomm.h" + +#include <array> +#include <string_view> + + +namespace osd { + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// base for virtual network interface handler + +class network_handler +{ +public: + network_handler() noexcept; + + virtual void recv_cb(u8 *buf, int len) = 0; + + std::array<u8, 6> const &get_mac() noexcept { return m_mac; } + +protected: + ~network_handler() = default; + + std::array<u8, 6> m_mac; +}; + + +// interface to network device + +class network_device +{ +public: + virtual ~network_device() = default; + + virtual void start() = 0; + virtual void stop() = 0; + virtual void poll() = 0; + virtual int send(const void *buf, int len) = 0; +}; + + +// description of an available network device + +struct network_device_info +{ + int id; + std::string_view description; +}; + +} // namespace osd + +#endif // MAME_OSD_INTERFACE_NETHANDLER_H diff --git a/src/osd/interface/uievents.h b/src/osd/interface/uievents.h new file mode 100644 index 00000000000..199d612713a --- /dev/null +++ b/src/osd/interface/uievents.h @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Vas Crabb +/*************************************************************************** + + uievents.h + + OSD UI event interfaces + +***************************************************************************/ +#ifndef MAME_OSD_INTERFACE_UIEVENTS_H +#define MAME_OSD_INTERFACE_UIEVENTS_H + + +//************************************************************************** +// FORWARD DECLARATIONS +//************************************************************************** + +class render_target; + + +namespace osd { + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// UI event handler interface + +class ui_event_handler +{ +protected: + ~ui_event_handler() = default; + +public: + enum class pointer + { + UNKNOWN, + MOUSE, + PEN, + TOUCH + }; + + // window events + virtual void push_window_focus_event(render_target *target) = 0; + virtual void push_window_defocus_event(render_target *target) = 0; + + // legacy mouse events + virtual void push_mouse_wheel_event(render_target *target, s32 x, s32 y, short delta, int lines) = 0; + + // pointer events + virtual void push_pointer_update(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 buttons, u32 pressed, u32 released, s16 clicks) = 0; + virtual void push_pointer_leave(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) = 0; + virtual void push_pointer_abort(render_target *target, pointer type, u16 ptrid, u16 device, s32 x, s32 y, u32 released, s16 clicks) = 0; + + // text input events + virtual void push_char_event(render_target *target, char32_t ch) = 0; +}; + +} // namespace osd + +#endif // MAME_OSD_INTERFACE_UIEVENTS_H diff --git a/src/osd/mac/macmain.cpp b/src/osd/mac/macmain.cpp index 170b5839f64..a778046c159 100644 --- a/src/osd/mac/macmain.cpp +++ b/src/osd/mac/macmain.cpp @@ -147,7 +147,6 @@ static void defines_verbose(void) osd_printf_verbose("\n"); osd_printf_verbose("Build defines 1: "); MACRO_VERBOSE(LSB_FIRST); - MACRO_VERBOSE(PTR64); MACRO_VERBOSE(MAME_NOASM); MACRO_VERBOSE(MAME_DEBUG); MACRO_VERBOSE(BIGENDIAN); diff --git a/src/osd/mac/osdmac.h b/src/osd/mac/osdmac.h index 04d5a0e1b2c..081c5757c17 100644 --- a/src/osd/mac/osdmac.h +++ b/src/osd/mac/osdmac.h @@ -68,10 +68,6 @@ public: virtual mac_options &options() override { return m_options; } -protected: - virtual void build_slider_list() override; - virtual void update_slider_list() override; - private: virtual void osd_exit() override; diff --git a/src/osd/mac/window.cpp b/src/osd/mac/window.cpp index b3ab9083a90..161bc8b677b 100644 --- a/src/osd/mac/window.cpp +++ b/src/osd/mac/window.cpp @@ -73,30 +73,6 @@ bool mac_osd_interface::window_init() } -void mac_osd_interface::update_slider_list() -{ - for (auto const &window : osd_common_t::window_list()) - { - // check if any window has dirty sliders - if (window->renderer().sliders_dirty()) - { - build_slider_list(); - return; - } - } -} - -void mac_osd_interface::build_slider_list() -{ - m_sliders.clear(); - - for (auto const &window : osd_common_t::window_list()) - { - std::vector<ui::menu_item> window_sliders = window->renderer().get_slider_list(); - m_sliders.insert(m_sliders.end(), window_sliders.begin(), window_sliders.end()); - } -} - void mac_osd_interface::process_events() { } @@ -106,6 +82,7 @@ bool mac_osd_interface::has_focus() const return true; } + //============================================================ // macwindow_exit // (main thread) @@ -223,7 +200,7 @@ void mac_window_info::modify_prescale(int dir) { int new_prescale = prescale(); - if (dir > 0 && prescale() < 3) + if (dir > 0 && prescale() < 8) new_prescale = prescale() + 1; if (dir < 0 && prescale() > 1) new_prescale = prescale() - 1; @@ -243,8 +220,8 @@ void mac_window_info::modify_prescale(int dir) notify_changed(); m_prescale = new_prescale; } - machine().ui().popup_time(1, "Prescale %d", prescale()); } + machine().ui().popup_time(1, "Prescale %d", prescale()); } //============================================================ diff --git a/src/osd/modules/debugger/debuggdbstub.cpp b/src/osd/modules/debugger/debuggdbstub.cpp index 409b6fa3bbb..729280f2134 100644 --- a/src/osd/modules/debugger/debuggdbstub.cpp +++ b/src/osd/modules/debugger/debuggdbstub.cpp @@ -21,6 +21,7 @@ #include "fileio.h" #include <cinttypes> +#include <string_view> namespace osd { @@ -47,71 +48,85 @@ static const char *const gdb_register_type_str[] = { struct gdb_register_map { const char *arch; - const char *feature; - struct gdb_register_description + struct gdb_feature { - const char *state_name; - const char *gdb_name; - bool stop_packet; - gdb_register_type gdb_type; - int override_bitsize; - - gdb_register_description(const char *_state_name=nullptr, const char *_gdb_name=nullptr, bool _stop_packet=false, gdb_register_type _gdb_type=TYPE_INT, int _override_bitsize=-1) - : state_name(_state_name) - , gdb_name(_gdb_name) - , stop_packet(_stop_packet) - , gdb_type(_gdb_type) - , override_bitsize(_override_bitsize) + const char *feature_name; + struct gdb_register_description + { + const char *state_name; + const char *gdb_name; + bool stop_packet; + gdb_register_type gdb_type; + int override_bitsize; + + gdb_register_description(const char *_state_name = nullptr, const char *_gdb_name = nullptr, bool _stop_packet = false, gdb_register_type _gdb_type = TYPE_INT, int _override_bitsize = -1) + : state_name(_state_name) + , gdb_name(_gdb_name) + , stop_packet(_stop_packet) + , gdb_type(_gdb_type) + , override_bitsize(_override_bitsize) + { + } + }; + std::vector<gdb_register_description> registers; + + gdb_feature(const char *_feature_name, std::initializer_list<gdb_register_description> _registers) + : feature_name(_feature_name) + , registers(_registers) { } }; - std::vector<gdb_register_description> registers; + std::vector<gdb_feature> features; }; //------------------------------------------------------------------------- static const gdb_register_map gdb_register_map_i486 = { "i386", - "org.gnu.gdb.i386.core", { - { "EAX", "eax", false, TYPE_INT }, - { "ECX", "ecx", false, TYPE_INT }, - { "EDX", "edx", false, TYPE_INT }, - { "EBX", "ebx", false, TYPE_INT }, - { "ESP", "esp", true, TYPE_DATA_POINTER }, - { "EBP", "ebp", true, TYPE_DATA_POINTER }, - { "ESI", "esi", false, TYPE_INT }, - { "EDI", "edi", false, TYPE_INT }, - { "EIP", "eip", true, TYPE_CODE_POINTER }, - { "EFLAGS", "eflags", false, TYPE_INT }, // TODO describe bitfield - { "CS", "cs", false, TYPE_INT }, - { "SS", "ss", false, TYPE_INT }, - { "DS", "ds", false, TYPE_INT }, - { "ES", "es", false, TYPE_INT }, - { "FS", "fs", false, TYPE_INT }, - { "GS", "gs", false, TYPE_INT }, - // TODO fix x87 registers! - // The x87 registers are just plain wrong for a few reasons: - // - The st* registers use a dummy variable in i386_device, so we - // don't retrieve the real value (also the bitsize is wrong); - // - The seg/off/op registers don't seem to be exported in the - // state. - { "ST0", "st0", false, TYPE_I387_EXT }, - { "ST1", "st1", false, TYPE_I387_EXT }, - { "ST2", "st2", false, TYPE_I387_EXT }, - { "ST3", "st3", false, TYPE_I387_EXT }, - { "ST4", "st4", false, TYPE_I387_EXT }, - { "ST5", "st5", false, TYPE_I387_EXT }, - { "ST6", "st6", false, TYPE_I387_EXT }, - { "ST7", "st7", false, TYPE_I387_EXT }, - { "x87_CW", "fctrl", false, TYPE_INT }, - { "x87_SW", "fstat", false, TYPE_INT }, - { "x87_TAG", "ftag", false, TYPE_INT }, - { "EAX", "fiseg", false, TYPE_INT }, - { "EAX", "fioff", false, TYPE_INT }, - { "EAX", "foseg", false, TYPE_INT }, - { "EAX", "fooff", false, TYPE_INT }, - { "EAX", "fop", false, TYPE_INT }, + { + "org.gnu.gdb.i386.core", + { + { "EAX", "eax", false, TYPE_INT }, + { "ECX", "ecx", false, TYPE_INT }, + { "EDX", "edx", false, TYPE_INT }, + { "EBX", "ebx", false, TYPE_INT }, + { "ESP", "esp", true, TYPE_DATA_POINTER }, + { "EBP", "ebp", true, TYPE_DATA_POINTER }, + { "ESI", "esi", false, TYPE_INT }, + { "EDI", "edi", false, TYPE_INT }, + { "EIP", "eip", true, TYPE_CODE_POINTER }, + { "EFLAGS", "eflags", false, TYPE_INT }, // TODO describe bitfield + { "CS", "cs", false, TYPE_INT }, + { "SS", "ss", false, TYPE_INT }, + { "DS", "ds", false, TYPE_INT }, + { "ES", "es", false, TYPE_INT }, + { "FS", "fs", false, TYPE_INT }, + { "GS", "gs", false, TYPE_INT }, + // TODO fix x87 registers! + // The x87 registers are just plain wrong for a few reasons: + // - The st* registers use a dummy variable in i386_device, so we + // don't retrieve the real value (also the bitsize is wrong); + // - The seg/off/op registers don't seem to be exported in the + // state. + { "ST0", "st0", false, TYPE_I387_EXT }, + { "ST1", "st1", false, TYPE_I387_EXT }, + { "ST2", "st2", false, TYPE_I387_EXT }, + { "ST3", "st3", false, TYPE_I387_EXT }, + { "ST4", "st4", false, TYPE_I387_EXT }, + { "ST5", "st5", false, TYPE_I387_EXT }, + { "ST6", "st6", false, TYPE_I387_EXT }, + { "ST7", "st7", false, TYPE_I387_EXT }, + { "x87_CW", "fctrl", false, TYPE_INT }, + { "x87_SW", "fstat", false, TYPE_INT }, + { "x87_TAG", "ftag", false, TYPE_INT }, + { "EAX", "fiseg", false, TYPE_INT }, + { "EAX", "fioff", false, TYPE_INT }, + { "EAX", "foseg", false, TYPE_INT }, + { "EAX", "fooff", false, TYPE_INT }, + { "EAX", "fop", false, TYPE_INT }, + } + } } }; @@ -119,25 +134,29 @@ static const gdb_register_map gdb_register_map_i486 = static const gdb_register_map gdb_register_map_arm7 = { "arm", - "org.gnu.gdb.arm.core", { - { "R0", "r0", false, TYPE_INT }, - { "R1", "r1", false, TYPE_INT }, - { "R2", "r2", false, TYPE_INT }, - { "R3", "r3", false, TYPE_INT }, - { "R4", "r4", false, TYPE_INT }, - { "R5", "r5", false, TYPE_INT }, - { "R6", "r6", false, TYPE_INT }, - { "R7", "r7", false, TYPE_INT }, - { "R8", "r8", false, TYPE_INT }, - { "R9", "r9", false, TYPE_INT }, - { "R10", "r10", false, TYPE_INT }, - { "R11", "r11", false, TYPE_INT }, - { "R12", "r12", false, TYPE_INT }, - { "R13", "sp", true, TYPE_DATA_POINTER }, - { "R14", "lr", true, TYPE_INT }, - { "R15", "pc", true, TYPE_CODE_POINTER }, - { "CPSR", "cpsr", false, TYPE_INT }, // TODO describe bitfield + { + "org.gnu.gdb.arm.core", + { + { "R0", "r0", false, TYPE_INT }, + { "R1", "r1", false, TYPE_INT }, + { "R2", "r2", false, TYPE_INT }, + { "R3", "r3", false, TYPE_INT }, + { "R4", "r4", false, TYPE_INT }, + { "R5", "r5", false, TYPE_INT }, + { "R6", "r6", false, TYPE_INT }, + { "R7", "r7", false, TYPE_INT }, + { "R8", "r8", false, TYPE_INT }, + { "R9", "r9", false, TYPE_INT }, + { "R10", "r10", false, TYPE_INT }, + { "R11", "r11", false, TYPE_INT }, + { "R12", "r12", false, TYPE_INT }, + { "R13", "sp", true, TYPE_DATA_POINTER }, + { "R14", "lr", true, TYPE_INT }, + { "R15", "pc", true, TYPE_CODE_POINTER }, + { "CPSR", "cpsr", false, TYPE_INT }, // TODO describe bitfield + } + } } }; @@ -145,46 +164,50 @@ static const gdb_register_map gdb_register_map_arm7 = static const gdb_register_map gdb_register_map_ppc601 = { "powerpc:common", - "org.gnu.gdb.power.core", { - { "R0", "r0", false, TYPE_INT }, - { "R1", "r1", false, TYPE_INT }, - { "R2", "r2", false, TYPE_INT }, - { "R3", "r3", false, TYPE_INT }, - { "R4", "r4", false, TYPE_INT }, - { "R5", "r5", false, TYPE_INT }, - { "R6", "r6", false, TYPE_INT }, - { "R7", "r7", false, TYPE_INT }, - { "R8", "r8", false, TYPE_INT }, - { "R9", "r9", false, TYPE_INT }, - { "R10", "r10", false, TYPE_INT }, - { "R11", "r11", false, TYPE_INT }, - { "R12", "r12", false, TYPE_INT }, - { "R13", "r13", false, TYPE_INT }, - { "R14", "r14", false, TYPE_INT }, - { "R15", "r15", false, TYPE_INT }, - { "R16", "r16", false, TYPE_INT }, - { "R17", "r17", false, TYPE_INT }, - { "R18", "r18", false, TYPE_INT }, - { "R19", "r19", false, TYPE_INT }, - { "R20", "r20", false, TYPE_INT }, - { "R21", "r21", false, TYPE_INT }, - { "R22", "r22", false, TYPE_INT }, - { "R23", "r23", false, TYPE_INT }, - { "R24", "r24", false, TYPE_INT }, - { "R25", "r25", false, TYPE_INT }, - { "R26", "r26", false, TYPE_INT }, - { "R27", "r27", false, TYPE_INT }, - { "R28", "r28", false, TYPE_INT }, - { "R29", "r29", false, TYPE_INT }, - { "R30", "r30", false, TYPE_INT }, - { "R31", "r31", false, TYPE_INT }, - { "PC", "pc", true, TYPE_CODE_POINTER }, - { "MSR", "msr", false, TYPE_INT }, - { "CR", "cr", false, TYPE_INT }, - { "LR", "lr", true, TYPE_CODE_POINTER }, - { "CTR", "ctr", false, TYPE_INT }, - { "XER", "xer", false, TYPE_INT }, + { + "org.gnu.gdb.power.core", + { + { "R0", "r0", false, TYPE_INT }, + { "R1", "r1", false, TYPE_INT }, + { "R2", "r2", false, TYPE_INT }, + { "R3", "r3", false, TYPE_INT }, + { "R4", "r4", false, TYPE_INT }, + { "R5", "r5", false, TYPE_INT }, + { "R6", "r6", false, TYPE_INT }, + { "R7", "r7", false, TYPE_INT }, + { "R8", "r8", false, TYPE_INT }, + { "R9", "r9", false, TYPE_INT }, + { "R10", "r10", false, TYPE_INT }, + { "R11", "r11", false, TYPE_INT }, + { "R12", "r12", false, TYPE_INT }, + { "R13", "r13", false, TYPE_INT }, + { "R14", "r14", false, TYPE_INT }, + { "R15", "r15", false, TYPE_INT }, + { "R16", "r16", false, TYPE_INT }, + { "R17", "r17", false, TYPE_INT }, + { "R18", "r18", false, TYPE_INT }, + { "R19", "r19", false, TYPE_INT }, + { "R20", "r20", false, TYPE_INT }, + { "R21", "r21", false, TYPE_INT }, + { "R22", "r22", false, TYPE_INT }, + { "R23", "r23", false, TYPE_INT }, + { "R24", "r24", false, TYPE_INT }, + { "R25", "r25", false, TYPE_INT }, + { "R26", "r26", false, TYPE_INT }, + { "R27", "r27", false, TYPE_INT }, + { "R28", "r28", false, TYPE_INT }, + { "R29", "r29", false, TYPE_INT }, + { "R30", "r30", false, TYPE_INT }, + { "R31", "r31", false, TYPE_INT }, + { "PC", "pc", true, TYPE_CODE_POINTER }, + { "MSR", "msr", false, TYPE_INT }, + { "CR", "cr", false, TYPE_INT }, + { "LR", "lr", true, TYPE_CODE_POINTER }, + { "CTR", "ctr", false, TYPE_INT }, + { "XER", "xer", false, TYPE_INT }, + } + } } }; @@ -192,43 +215,78 @@ static const gdb_register_map gdb_register_map_ppc601 = static const gdb_register_map gdb_register_map_r4600 = { "mips", - "org.gnu.gdb.mips.cpu", { - { "zero", "r0", false, TYPE_INT, 32 }, - { "at", "r1", false, TYPE_INT, 32 }, - { "v0", "r2", false, TYPE_INT, 32 }, - { "v1", "r3", false, TYPE_INT, 32 }, - { "a0", "r4", false, TYPE_INT, 32 }, - { "a1", "r5", false, TYPE_INT, 32 }, - { "a2", "r6", false, TYPE_INT, 32 }, - { "a3", "r7", false, TYPE_INT, 32 }, - { "t0", "r8", false, TYPE_INT, 32 }, - { "t1", "r9", false, TYPE_INT, 32 }, - { "t2", "r10", false, TYPE_INT, 32 }, - { "t3", "r11", false, TYPE_INT, 32 }, - { "t4", "r12", false, TYPE_INT, 32 }, - { "t5", "r13", false, TYPE_INT, 32 }, - { "t6", "r14", false, TYPE_INT, 32 }, - { "t7", "r15", false, TYPE_INT, 32 }, - { "s0", "r16", false, TYPE_INT, 32 }, - { "s1", "r17", false, TYPE_INT, 32 }, - { "s2", "r18", false, TYPE_INT, 32 }, - { "s3", "r19", false, TYPE_INT, 32 }, - { "s4", "r20", false, TYPE_INT, 32 }, - { "s5", "r21", false, TYPE_INT, 32 }, - { "s6", "r22", false, TYPE_INT, 32 }, - { "s7", "r23", false, TYPE_INT, 32 }, - { "t8", "r24", false, TYPE_INT, 32 }, - { "t9", "r25", false, TYPE_INT, 32 }, - { "k0", "r26", false, TYPE_INT, 32 }, - { "k1", "r27", false, TYPE_INT, 32 }, - { "gp", "r28", false, TYPE_INT, 32 }, - { "sp", "r29", false, TYPE_INT, 32 }, - { "fp", "r30", false, TYPE_INT, 32 }, - { "ra", "r31", false, TYPE_INT, 32 }, - { "LO", "lo", false, TYPE_INT, 32 }, - { "HI", "hi", false, TYPE_INT, 32 }, - { "PC", "pc", true, TYPE_CODE_POINTER, 32 }, + { + "org.gnu.gdb.mips.cpu", + { + { "zero", "r0", false, TYPE_INT, 32 }, + { "at", "r1", false, TYPE_INT, 32 }, + { "v0", "r2", false, TYPE_INT, 32 }, + { "v1", "r3", false, TYPE_INT, 32 }, + { "a0", "r4", false, TYPE_INT, 32 }, + { "a1", "r5", false, TYPE_INT, 32 }, + { "a2", "r6", false, TYPE_INT, 32 }, + { "a3", "r7", false, TYPE_INT, 32 }, + { "t0", "r8", false, TYPE_INT, 32 }, + { "t1", "r9", false, TYPE_INT, 32 }, + { "t2", "r10", false, TYPE_INT, 32 }, + { "t3", "r11", false, TYPE_INT, 32 }, + { "t4", "r12", false, TYPE_INT, 32 }, + { "t5", "r13", false, TYPE_INT, 32 }, + { "t6", "r14", false, TYPE_INT, 32 }, + { "t7", "r15", false, TYPE_INT, 32 }, + { "s0", "r16", false, TYPE_INT, 32 }, + { "s1", "r17", false, TYPE_INT, 32 }, + { "s2", "r18", false, TYPE_INT, 32 }, + { "s3", "r19", false, TYPE_INT, 32 }, + { "s4", "r20", false, TYPE_INT, 32 }, + { "s5", "r21", false, TYPE_INT, 32 }, + { "s6", "r22", false, TYPE_INT, 32 }, + { "s7", "r23", false, TYPE_INT, 32 }, + { "t8", "r24", false, TYPE_INT, 32 }, + { "t9", "r25", false, TYPE_INT, 32 }, + { "k0", "r26", false, TYPE_INT, 32 }, + { "k1", "r27", false, TYPE_INT, 32 }, + { "gp", "r28", false, TYPE_INT, 32 }, + { "sp", "r29", false, TYPE_INT, 32 }, + { "fp", "r30", false, TYPE_INT, 32 }, + { "ra", "r31", false, TYPE_INT, 32 }, + { "LO", "lo", false, TYPE_INT, 32 }, + { "HI", "hi", false, TYPE_INT, 32 }, + { "PC", "pc", true, TYPE_CODE_POINTER, 32 }, + } + } + } +}; + +//------------------------------------------------------------------------- +static const gdb_register_map gdb_register_map_m68030 = +{ + "m68k", + { + { + "org.gnu.gdb.m68k.core", + { + { "D0", "d0", false, TYPE_INT }, + { "D1", "d1", false, TYPE_INT }, + { "D2", "d2", false, TYPE_INT }, + { "D3", "d3", false, TYPE_INT }, + { "D4", "d4", false, TYPE_INT }, + { "D5", "d5", false, TYPE_INT }, + { "D6", "d6", false, TYPE_INT }, + { "D7", "d7", false, TYPE_INT }, + { "A0", "a0", false, TYPE_INT }, + { "A1", "a1", false, TYPE_INT }, + { "A2", "a2", false, TYPE_INT }, + { "A3", "a3", false, TYPE_INT }, + { "A4", "a4", false, TYPE_INT }, + { "A5", "a5", false, TYPE_INT }, + { "A6", "fp", true, TYPE_INT }, + { "SP", "sp", true, TYPE_INT }, + { "SR", "ps", false, TYPE_INT }, // NOTE GDB named it ps, but it's actually sr + { "CURPC","pc", true, TYPE_CODE_POINTER }, + } + } } }; @@ -236,26 +294,30 @@ static const gdb_register_map gdb_register_map_r4600 = static const gdb_register_map gdb_register_map_m68020pmmu = { "m68k", - "org.gnu.gdb.m68k.core", { - { "D0", "d0", false, TYPE_INT }, - { "D1", "d1", false, TYPE_INT }, - { "D2", "d2", false, TYPE_INT }, - { "D3", "d3", false, TYPE_INT }, - { "D4", "d4", false, TYPE_INT }, - { "D5", "d5", false, TYPE_INT }, - { "D6", "d6", false, TYPE_INT }, - { "D7", "d7", false, TYPE_INT }, - { "A0", "a0", false, TYPE_INT }, - { "A1", "a1", false, TYPE_INT }, - { "A2", "a2", false, TYPE_INT }, - { "A3", "a3", false, TYPE_INT }, - { "A4", "a4", false, TYPE_INT }, - { "A5", "a5", false, TYPE_INT }, - { "A6", "fp", true, TYPE_INT }, - { "A7", "sp", true, TYPE_INT }, - { "SR", "ps", false, TYPE_INT }, // NOTE GDB named it ps, but it's actually sr - { "PC", "pc", true, TYPE_CODE_POINTER }, + { + "org.gnu.gdb.m68k.core", + { + { "D0", "d0", false, TYPE_INT }, + { "D1", "d1", false, TYPE_INT }, + { "D2", "d2", false, TYPE_INT }, + { "D3", "d3", false, TYPE_INT }, + { "D4", "d4", false, TYPE_INT }, + { "D5", "d5", false, TYPE_INT }, + { "D6", "d6", false, TYPE_INT }, + { "D7", "d7", false, TYPE_INT }, + { "A0", "a0", false, TYPE_INT }, + { "A1", "a1", false, TYPE_INT }, + { "A2", "a2", false, TYPE_INT }, + { "A3", "a3", false, TYPE_INT }, + { "A4", "a4", false, TYPE_INT }, + { "A5", "a5", false, TYPE_INT }, + { "A6", "fp", true, TYPE_INT }, + { "SP", "sp", true, TYPE_INT }, + { "SR", "ps", false, TYPE_INT }, // NOTE GDB named it ps, but it's actually sr + { "CURPC","pc", true, TYPE_CODE_POINTER }, + } + } } }; @@ -263,27 +325,31 @@ static const gdb_register_map gdb_register_map_m68020pmmu = static const gdb_register_map gdb_register_map_m68000 = { "m68k", - "org.gnu.gdb.m68k.core", { - { "D0", "d0", false, TYPE_INT }, - { "D1", "d1", false, TYPE_INT }, - { "D2", "d2", false, TYPE_INT }, - { "D3", "d3", false, TYPE_INT }, - { "D4", "d4", false, TYPE_INT }, - { "D5", "d5", false, TYPE_INT }, - { "D6", "d6", false, TYPE_INT }, - { "D7", "d7", false, TYPE_INT }, - { "A0", "a0", false, TYPE_INT }, - { "A1", "a1", false, TYPE_INT }, - { "A2", "a2", false, TYPE_INT }, - { "A3", "a3", false, TYPE_INT }, - { "A4", "a4", false, TYPE_INT }, - { "A5", "a5", false, TYPE_INT }, - { "A6", "fp", true, TYPE_INT }, - { "A7", "sp", true, TYPE_INT }, - { "SR", "ps", false, TYPE_INT }, // NOTE GDB named it ps, but it's actually sr - { "PC", "pc", true, TYPE_CODE_POINTER }, - //NOTE m68-elf-gdb complains about fpcontrol register not present but 68000 doesn't have floating point so... + { + "org.gnu.gdb.m68k.core", + { + { "D0", "d0", false, TYPE_INT }, + { "D1", "d1", false, TYPE_INT }, + { "D2", "d2", false, TYPE_INT }, + { "D3", "d3", false, TYPE_INT }, + { "D4", "d4", false, TYPE_INT }, + { "D5", "d5", false, TYPE_INT }, + { "D6", "d6", false, TYPE_INT }, + { "D7", "d7", false, TYPE_INT }, + { "A0", "a0", false, TYPE_INT }, + { "A1", "a1", false, TYPE_INT }, + { "A2", "a2", false, TYPE_INT }, + { "A3", "a3", false, TYPE_INT }, + { "A4", "a4", false, TYPE_INT }, + { "A5", "a5", false, TYPE_INT }, + { "A6", "fp", true, TYPE_INT }, + { "SP", "sp", true, TYPE_INT }, + { "SR", "ps", false, TYPE_INT }, // NOTE GDB named it ps, but it's actually sr + { "CURPC","pc", true, TYPE_CODE_POINTER }, + //NOTE m68-elf-gdb complains about fpcontrol register not present but 68000 doesn't have floating point so... + } + } } }; @@ -291,20 +357,24 @@ static const gdb_register_map gdb_register_map_m68000 = static const gdb_register_map gdb_register_map_z80 = { "z80", - "mame.z80", { - { "AF", "af", false, TYPE_INT }, - { "BC", "bc", false, TYPE_INT }, - { "DE", "de", false, TYPE_INT }, - { "HL", "hl", false, TYPE_INT }, - { "AF2", "af'", false, TYPE_INT }, - { "BC2", "bc'", false, TYPE_INT }, - { "DE2", "de'", false, TYPE_INT }, - { "HL2", "hl'", false, TYPE_INT }, - { "IX", "ix", false, TYPE_INT }, - { "IY", "iy", false, TYPE_INT }, - { "SP", "sp", true, TYPE_DATA_POINTER }, - { "PC", "pc", true, TYPE_CODE_POINTER }, + { + "mame.z80", + { + { "AF", "af", false, TYPE_INT }, + { "BC", "bc", false, TYPE_INT }, + { "DE", "de", false, TYPE_INT }, + { "HL", "hl", false, TYPE_INT }, + { "AF2", "af'", false, TYPE_INT }, + { "BC2", "bc'", false, TYPE_INT }, + { "DE2", "de'", false, TYPE_INT }, + { "HL2", "hl'", false, TYPE_INT }, + { "IX", "ix", false, TYPE_INT }, + { "IY", "iy", false, TYPE_INT }, + { "SP", "sp", true, TYPE_DATA_POINTER }, + { "PC", "pc", true, TYPE_CODE_POINTER }, + } + } } }; @@ -312,14 +382,18 @@ static const gdb_register_map gdb_register_map_z80 = static const gdb_register_map gdb_register_map_m6502 = { "m6502", - "mame.m6502", { - { "A", "a", false, TYPE_INT }, - { "X", "x", false, TYPE_INT }, - { "Y", "y", false, TYPE_INT }, - { "P", "p", false, TYPE_INT }, - { "SP", "sp", true, TYPE_DATA_POINTER }, - { "PC", "pc", true, TYPE_CODE_POINTER }, + { + "mame.m6502", + { + { "A", "a", false, TYPE_INT }, + { "X", "x", false, TYPE_INT }, + { "Y", "y", false, TYPE_INT }, + { "P", "p", false, TYPE_INT }, + { "SP", "sp", true, TYPE_DATA_POINTER }, + { "PC", "pc", true, TYPE_CODE_POINTER }, + } + } } }; @@ -328,18 +402,22 @@ static const gdb_register_map gdb_register_map_m6502 = static const gdb_register_map gdb_register_map_m6809 = { "m6809", - "mame.m6809", { - { "A", "a", false, TYPE_INT }, - { "B", "b", false, TYPE_INT }, - { "D", "d", false, TYPE_INT }, - { "X", "x", false, TYPE_INT }, - { "Y", "y", false, TYPE_INT }, - { "U", "u", true, TYPE_DATA_POINTER }, - { "PC", "pc", true, TYPE_CODE_POINTER }, - { "S", "s", true, TYPE_DATA_POINTER }, - { "CC", "cc", false, TYPE_INT }, // TODO describe bitfield - { "DP", "dp", false, TYPE_INT }, + { + "mame.m6809", + { + { "A", "a", false, TYPE_INT }, + { "B", "b", false, TYPE_INT }, + { "D", "d", false, TYPE_INT }, + { "X", "x", false, TYPE_INT }, + { "Y", "y", false, TYPE_INT }, + { "U", "u", true, TYPE_DATA_POINTER }, + { "PC", "pc", true, TYPE_CODE_POINTER }, + { "S", "s", true, TYPE_DATA_POINTER }, + { "CC", "cc", false, TYPE_INT }, // TODO describe bitfield + { "DP", "dp", false, TYPE_INT }, + } + } } }; @@ -348,64 +426,68 @@ static const gdb_register_map gdb_register_map_m6809 = static const gdb_register_map gdb_register_map_score7 = { "score7", - "mame.score7", { - { "r0", "r0", true, TYPE_DATA_POINTER }, - { "r1", "r1", false, TYPE_INT }, - { "r2", "r2", false, TYPE_INT }, - { "r3", "r3", false, TYPE_INT }, - { "r4", "r4", false, TYPE_INT }, - { "r5", "r5", false, TYPE_INT }, - { "r6", "r6", false, TYPE_INT }, - { "r7", "r7", false, TYPE_INT }, - { "r8", "r8", false, TYPE_INT }, - { "r9", "r9", false, TYPE_INT }, - { "r10", "r10", false, TYPE_INT }, - { "r11", "r11", false, TYPE_INT }, - { "r12", "r12", false, TYPE_INT }, - { "r13", "r13", false, TYPE_INT }, - { "r14", "r14", false, TYPE_INT }, - { "r15", "r15", false, TYPE_INT }, - { "r16", "r16", false, TYPE_INT }, - { "r17", "r17", false, TYPE_INT }, - { "r18", "r18", false, TYPE_INT }, - { "r19", "r19", false, TYPE_INT }, - { "r20", "r20", false, TYPE_INT }, - { "r21", "r21", false, TYPE_INT }, - { "r22", "r22", false, TYPE_INT }, - { "r23", "r23", false, TYPE_INT }, - { "r24", "r24", false, TYPE_INT }, - { "r25", "r25", false, TYPE_INT }, - { "r26", "r26", false, TYPE_INT }, - { "r27", "r27", false, TYPE_INT }, - { "r28", "r28", false, TYPE_INT }, - { "r29", "r29", false, TYPE_INT }, - { "r30", "r30", false, TYPE_INT }, - { "r31", "r31", false, TYPE_INT }, - { "cr0", "PSR", false, TYPE_INT }, - { "cr1", "COND", false, TYPE_INT }, - { "cr2", "ECR", false, TYPE_INT }, - { "cr3", "EXCPVEC", false, TYPE_INT }, - { "cr4", "CCR", false, TYPE_INT }, - { "cr5", "EPC", false, TYPE_INT }, - { "cr6", "EMA", false, TYPE_INT }, - { "cr7", "TLBLOCK", false, TYPE_INT }, - { "cr8", "TLBPT", false, TYPE_INT }, - { "cr9", "PEADDR", false, TYPE_INT }, - { "cr10", "TLBRPT", false, TYPE_INT }, - { "cr11", "PEVN", false, TYPE_INT }, - { "cr12", "PECTX", false, TYPE_INT }, - { "cr15", "LIMPFN", false, TYPE_INT }, - { "cr16", "LDMPFN", false, TYPE_INT }, - { "cr18", "PREV", false, TYPE_INT }, - { "cr29", "DREG", false, TYPE_INT }, - { "PC", "PC", true, TYPE_CODE_POINTER }, // actually Debug exception program counter (DEPC) - { "cr31", "DSAVE", false, TYPE_INT }, - { "sr0", "COUNTER", false, TYPE_INT }, - { "sr1", "LDCR", false, TYPE_INT }, - { "sr2", "STCR", false, TYPE_INT }, - { "ceh", "CEH", false, TYPE_INT }, - { "cel", "CEL", false, TYPE_INT }, + { + "mame.score7", + { + { "r0", "r0", true, TYPE_DATA_POINTER }, + { "r1", "r1", false, TYPE_INT }, + { "r2", "r2", false, TYPE_INT }, + { "r3", "r3", false, TYPE_INT }, + { "r4", "r4", false, TYPE_INT }, + { "r5", "r5", false, TYPE_INT }, + { "r6", "r6", false, TYPE_INT }, + { "r7", "r7", false, TYPE_INT }, + { "r8", "r8", false, TYPE_INT }, + { "r9", "r9", false, TYPE_INT }, + { "r10", "r10", false, TYPE_INT }, + { "r11", "r11", false, TYPE_INT }, + { "r12", "r12", false, TYPE_INT }, + { "r13", "r13", false, TYPE_INT }, + { "r14", "r14", false, TYPE_INT }, + { "r15", "r15", false, TYPE_INT }, + { "r16", "r16", false, TYPE_INT }, + { "r17", "r17", false, TYPE_INT }, + { "r18", "r18", false, TYPE_INT }, + { "r19", "r19", false, TYPE_INT }, + { "r20", "r20", false, TYPE_INT }, + { "r21", "r21", false, TYPE_INT }, + { "r22", "r22", false, TYPE_INT }, + { "r23", "r23", false, TYPE_INT }, + { "r24", "r24", false, TYPE_INT }, + { "r25", "r25", false, TYPE_INT }, + { "r26", "r26", false, TYPE_INT }, + { "r27", "r27", false, TYPE_INT }, + { "r28", "r28", false, TYPE_INT }, + { "r29", "r29", false, TYPE_INT }, + { "r30", "r30", false, TYPE_INT }, + { "r31", "r31", false, TYPE_INT }, + { "cr0", "PSR", false, TYPE_INT }, + { "cr1", "COND", false, TYPE_INT }, + { "cr2", "ECR", false, TYPE_INT }, + { "cr3", "EXCPVEC", false, TYPE_INT }, + { "cr4", "CCR", false, TYPE_INT }, + { "cr5", "EPC", false, TYPE_INT }, + { "cr6", "EMA", false, TYPE_INT }, + { "cr7", "TLBLOCK", false, TYPE_INT }, + { "cr8", "TLBPT", false, TYPE_INT }, + { "cr9", "PEADDR", false, TYPE_INT }, + { "cr10", "TLBRPT", false, TYPE_INT }, + { "cr11", "PEVN", false, TYPE_INT }, + { "cr12", "PECTX", false, TYPE_INT }, + { "cr15", "LIMPFN", false, TYPE_INT }, + { "cr16", "LDMPFN", false, TYPE_INT }, + { "cr18", "PREV", false, TYPE_INT }, + { "cr29", "DREG", false, TYPE_INT }, + { "PC", "PC", true, TYPE_CODE_POINTER }, // actually Debug exception program counter (DEPC) + { "cr31", "DSAVE", false, TYPE_INT }, + { "sr0", "COUNTER", false, TYPE_INT }, + { "sr1", "LDCR", false, TYPE_INT }, + { "sr2", "STCR", false, TYPE_INT }, + { "ceh", "CEH", false, TYPE_INT }, + { "cel", "CEL", false, TYPE_INT }, + } + } } }; @@ -414,57 +496,156 @@ static const gdb_register_map gdb_register_map_score7 = static const gdb_register_map gdb_register_map_nios2 = { "nios2", - "org.gnu.gdb.nios2.cpu", { - { "zero", "zero", false, TYPE_INT }, - { "at", "at", false, TYPE_INT }, - { "r2", "r2", false, TYPE_INT }, - { "r3", "r3", false, TYPE_INT }, - { "r4", "r4", false, TYPE_INT }, - { "r5", "r5", false, TYPE_INT }, - { "r6", "r6", false, TYPE_INT }, - { "r7", "r7", false, TYPE_INT }, - { "r8", "r8", false, TYPE_INT }, - { "r9", "r9", false, TYPE_INT }, - { "r10", "r10", false, TYPE_INT }, - { "r11", "r11", false, TYPE_INT }, - { "r12", "r12", false, TYPE_INT }, - { "r13", "r13", false, TYPE_INT }, - { "r14", "r14", false, TYPE_INT }, - { "r15", "r15", false, TYPE_INT }, - { "r16", "r16", false, TYPE_INT }, - { "r17", "r17", false, TYPE_INT }, - { "r18", "r18", false, TYPE_INT }, - { "r19", "r19", false, TYPE_INT }, - { "r20", "r20", false, TYPE_INT }, - { "r21", "r21", false, TYPE_INT }, - { "r22", "r22", false, TYPE_INT }, - { "r23", "r23", false, TYPE_INT }, - { "et", "et", false, TYPE_INT }, - { "bt", "bt", false, TYPE_INT }, - { "gp", "gp", false, TYPE_DATA_POINTER }, - { "sp", "sp", true, TYPE_DATA_POINTER }, - { "fp", "fp", false, TYPE_DATA_POINTER }, - { "ea", "ea", false, TYPE_CODE_POINTER }, - { "ba", "sstatus", false, TYPE_INT }, // this is Altera's fault - { "ra", "ra", false, TYPE_CODE_POINTER }, - { "status", "status", false, TYPE_INT }, - { "estatus", "estatus", false, TYPE_INT }, - { "bstatus", "bstatus", false, TYPE_INT }, - { "ienable", "ienable", false, TYPE_INT }, - { "ipending", "ipending", false, TYPE_INT }, - { "cpuid", "cpuid", false, TYPE_INT }, - { "ctl6", "ctl6", false, TYPE_INT }, - { "exception","exception",false, TYPE_INT }, - { "pteaddr", "pteaddr", false, TYPE_INT }, - { "tlbacc", "tlbacc", false, TYPE_INT }, - { "tlbmisc", "tlbmisc", false, TYPE_INT }, - { "eccinj", "eccinj", false, TYPE_INT }, - { "badaddr", "badaddr", false, TYPE_INT }, - { "config", "config", false, TYPE_INT }, - { "mpubase", "mpubase", false, TYPE_INT }, - { "mpuacc", "mpuacc", false, TYPE_INT }, - { "PC", "pc", true, TYPE_CODE_POINTER }, + { + "org.gnu.gdb.nios2.cpu", + { + { "zero", "zero", false, TYPE_INT }, + { "at", "at", false, TYPE_INT }, + { "r2", "r2", false, TYPE_INT }, + { "r3", "r3", false, TYPE_INT }, + { "r4", "r4", false, TYPE_INT }, + { "r5", "r5", false, TYPE_INT }, + { "r6", "r6", false, TYPE_INT }, + { "r7", "r7", false, TYPE_INT }, + { "r8", "r8", false, TYPE_INT }, + { "r9", "r9", false, TYPE_INT }, + { "r10", "r10", false, TYPE_INT }, + { "r11", "r11", false, TYPE_INT }, + { "r12", "r12", false, TYPE_INT }, + { "r13", "r13", false, TYPE_INT }, + { "r14", "r14", false, TYPE_INT }, + { "r15", "r15", false, TYPE_INT }, + { "r16", "r16", false, TYPE_INT }, + { "r17", "r17", false, TYPE_INT }, + { "r18", "r18", false, TYPE_INT }, + { "r19", "r19", false, TYPE_INT }, + { "r20", "r20", false, TYPE_INT }, + { "r21", "r21", false, TYPE_INT }, + { "r22", "r22", false, TYPE_INT }, + { "r23", "r23", false, TYPE_INT }, + { "et", "et", false, TYPE_INT }, + { "bt", "bt", false, TYPE_INT }, + { "gp", "gp", false, TYPE_DATA_POINTER }, + { "sp", "sp", true, TYPE_DATA_POINTER }, + { "fp", "fp", false, TYPE_DATA_POINTER }, + { "ea", "ea", false, TYPE_CODE_POINTER }, + { "ba", "sstatus", false, TYPE_INT }, // this is Altera's fault + { "ra", "ra", false, TYPE_CODE_POINTER }, + { "status", "status", false, TYPE_INT }, + { "estatus", "estatus", false, TYPE_INT }, + { "bstatus", "bstatus", false, TYPE_INT }, + { "ienable", "ienable", false, TYPE_INT }, + { "ipending", "ipending", false, TYPE_INT }, + { "cpuid", "cpuid", false, TYPE_INT }, + { "ctl6", "ctl6", false, TYPE_INT }, + { "exception","exception",false, TYPE_INT }, + { "pteaddr", "pteaddr", false, TYPE_INT }, + { "tlbacc", "tlbacc", false, TYPE_INT }, + { "tlbmisc", "tlbmisc", false, TYPE_INT }, + { "eccinj", "eccinj", false, TYPE_INT }, + { "badaddr", "badaddr", false, TYPE_INT }, + { "config", "config", false, TYPE_INT }, + { "mpubase", "mpubase", false, TYPE_INT }, + { "mpuacc", "mpuacc", false, TYPE_INT }, + { "PC", "pc", true, TYPE_CODE_POINTER }, + } + } + } +}; + +//------------------------------------------------------------------------- +static const gdb_register_map gdb_register_map_psxcpu = +{ + "mips", + { + { + "org.gnu.gdb.mips.cpu", + { + { "zero", "r0", false, TYPE_INT }, + { "at", "r1", false, TYPE_INT }, + { "v0", "r2", false, TYPE_INT }, + { "v1", "r3", false, TYPE_INT }, + { "a0", "r4", false, TYPE_INT }, + { "a1", "r5", false, TYPE_INT }, + { "a2", "r6", false, TYPE_INT }, + { "a3", "r7", false, TYPE_INT }, + { "t0", "r8", false, TYPE_INT }, + { "t1", "r9", false, TYPE_INT }, + { "t2", "r10", false, TYPE_INT }, + { "t3", "r11", false, TYPE_INT }, + { "t4", "r12", false, TYPE_INT }, + { "t5", "r13", false, TYPE_INT }, + { "t6", "r14", false, TYPE_INT }, + { "t7", "r15", false, TYPE_INT }, + { "s0", "r16", false, TYPE_INT }, + { "s1", "r17", false, TYPE_INT }, + { "s2", "r18", false, TYPE_INT }, + { "s3", "r19", false, TYPE_INT }, + { "s4", "r20", false, TYPE_INT }, + { "s5", "r21", false, TYPE_INT }, + { "s6", "r22", false, TYPE_INT }, + { "s7", "r23", false, TYPE_INT }, + { "t8", "r24", false, TYPE_INT }, + { "t9", "r25", false, TYPE_INT }, + { "k0", "r26", false, TYPE_INT }, + { "k1", "r27", false, TYPE_INT }, + { "gp", "r28", false, TYPE_INT }, + { "sp", "r29", false, TYPE_INT }, + { "fp", "r30", false, TYPE_INT }, + { "ra", "r31", false, TYPE_CODE_POINTER }, + { "lo", "lo", false, TYPE_INT }, + { "hi", "hi", false, TYPE_INT }, + { "pc", "pc", true, TYPE_CODE_POINTER }, + } + }, + { + "org.gnu.gdb.mips.cp0", + { + { "SR", "status", false, TYPE_INT }, + { "BadA", "badvaddr", false, TYPE_INT }, + { "Cause", "cause", false, TYPE_INT }, + } + }, + { + "org.gnu.gdb.mips.fpu", + { + { "zero", "f0", false, TYPE_INT }, + { "zero", "f1", false, TYPE_INT }, + { "zero", "f2", false, TYPE_INT }, + { "zero", "f3", false, TYPE_INT }, + { "zero", "f4", false, TYPE_INT }, + { "zero", "f5", false, TYPE_INT }, + { "zero", "f6", false, TYPE_INT }, + { "zero", "f7", false, TYPE_INT }, + { "zero", "f8", false, TYPE_INT }, + { "zero", "f9", false, TYPE_INT }, + { "zero", "f10", false, TYPE_INT }, + { "zero", "f11", false, TYPE_INT }, + { "zero", "f12", false, TYPE_INT }, + { "zero", "f13", false, TYPE_INT }, + { "zero", "f14", false, TYPE_INT }, + { "zero", "f15", false, TYPE_INT }, + { "zero", "f16", false, TYPE_INT }, + { "zero", "f17", false, TYPE_INT }, + { "zero", "f18", false, TYPE_INT }, + { "zero", "f19", false, TYPE_INT }, + { "zero", "f20", false, TYPE_INT }, + { "zero", "f21", false, TYPE_INT }, + { "zero", "f22", false, TYPE_INT }, + { "zero", "f23", false, TYPE_INT }, + { "zero", "f24", false, TYPE_INT }, + { "zero", "f25", false, TYPE_INT }, + { "zero", "f26", false, TYPE_INT }, + { "zero", "f27", false, TYPE_INT }, + { "zero", "f28", false, TYPE_INT }, + { "zero", "f29", false, TYPE_INT }, + { "zero", "f30", false, TYPE_INT }, + { "zero", "f31", false, TYPE_INT }, + { "zero", "fcsr", false, TYPE_INT }, + { "zero", "fir" , false, TYPE_INT }, + } + } } }; @@ -474,23 +655,39 @@ static const std::map<std::string, const gdb_register_map &> gdb_register_maps = { "arm7_le", gdb_register_map_arm7 }, { "r4600", gdb_register_map_r4600 }, { "ppc601", gdb_register_map_ppc601 }, + { "m68030", gdb_register_map_m68030 }, { "m68020pmmu", gdb_register_map_m68020pmmu }, { "m68000", gdb_register_map_m68000 }, { "z80", gdb_register_map_z80 }, + { "z80n", gdb_register_map_z80 }, { "z84c015", gdb_register_map_z80 }, { "m6502", gdb_register_map_m6502 }, + { "m6507", gdb_register_map_m6502 }, + { "m6510", gdb_register_map_m6502 }, + { "m65ce02", gdb_register_map_m6502 }, { "rp2a03", gdb_register_map_m6502 }, + { "w65c02", gdb_register_map_m6502 }, + { "w65c02s", gdb_register_map_m6502 }, { "m6809", gdb_register_map_m6809 }, { "score7", gdb_register_map_score7 }, { "nios2", gdb_register_map_nios2 }, + { "cxd8530q", gdb_register_map_psxcpu }, + { "cxd8530aq", gdb_register_map_psxcpu }, + { "cxd8530bq", gdb_register_map_psxcpu }, + { "cxd8530cq", gdb_register_map_psxcpu }, + { "cxd8606q", gdb_register_map_psxcpu }, + { "cxd8606aq", gdb_register_map_psxcpu }, + { "cxd8606bq", gdb_register_map_psxcpu }, + { "cxd8606cq", gdb_register_map_psxcpu }, + { "cxd8661r", gdb_register_map_psxcpu }, }; //------------------------------------------------------------------------- class debug_gdbstub : public osd_module, public debug_module { public: - debug_gdbstub() - : osd_module(OSD_DEBUG_PROVIDER, "gdbstub"), debug_module(), + debug_gdbstub() : + osd_module(OSD_DEBUG_PROVIDER, "gdbstub"), debug_module(), m_readbuf_state(PACKET_START), m_machine(nullptr), m_maincpu(nullptr), @@ -499,6 +696,7 @@ public: m_address_space(nullptr), m_debugger_cpu(nullptr), m_debugger_console(nullptr), + m_debugger_host(), m_debugger_port(0), m_socket(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE), m_is_be(false), @@ -582,7 +780,7 @@ public: int readchar(); - void send_reply(const char *str); + void send_reply(std::string_view str); void send_stop_packet(); private: @@ -593,6 +791,7 @@ private: address_space *m_address_space; debugger_cpu *m_debugger_cpu; debugger_console *m_debugger_console; + std::string m_debugger_host; int m_debugger_port; emu_file m_socket; bool m_is_be; @@ -604,6 +803,7 @@ private: struct gdb_register { + std::string gdb_feature_name; std::string gdb_name; int gdb_regnum; gdb_register_type gdb_type; @@ -613,7 +813,6 @@ private: std::vector<gdb_register> m_gdb_registers; std::set<int> m_stop_reply_registers; std::string m_gdb_arch; - std::string m_gdb_feature; std::map<offs_t, uint64_t> m_address_map; @@ -635,6 +834,7 @@ private: //------------------------------------------------------------------------- int debug_gdbstub::init(osd_interface &osd, const osd_options &options) { + m_debugger_host = options.debugger_host(); m_debugger_port = options.debugger_port(); return 0; } @@ -671,11 +871,11 @@ int debug_gdbstub::readchar() } //------------------------------------------------------------------------- -static std::string escape_packet(const std::string src) +static std::string escape_packet(std::string_view src) { std::string result; result.reserve(src.length()); - for ( char ch: src ) + for ( char ch : src ) { if ( ch == '#' || ch == '$' || ch == '}' ) { @@ -696,11 +896,23 @@ void debug_gdbstub::generate_target_xml() target_xml += "<?xml version=\"1.0\"?>\n"; target_xml += "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"; target_xml += "<target version=\"1.0\">\n"; - target_xml += string_format("<architecture>%s</architecture>\n", m_gdb_arch.c_str()); - target_xml += string_format(" <feature name=\"%s\">\n", m_gdb_feature.c_str()); + target_xml += string_format("<architecture>%s</architecture>\n", m_gdb_arch); + std::string feature_name; for ( const auto ®: m_gdb_registers ) - target_xml += string_format(" <reg name=\"%s\" bitsize=\"%d\" type=\"%s\"/>\n", reg.gdb_name.c_str(), reg.gdb_bitsize, gdb_register_type_str[reg.gdb_type]); - target_xml += " </feature>\n"; + { + if (feature_name != reg.gdb_feature_name) + { + if (!feature_name.empty()) + target_xml += " </feature>\n"; + + feature_name = reg.gdb_feature_name; + target_xml += string_format(" <feature name=\"%s\">\n", feature_name); + } + + target_xml += string_format(" <reg name=\"%s\" bitsize=\"%d\" type=\"%s\"/>\n", reg.gdb_name, reg.gdb_bitsize, gdb_register_type_str[reg.gdb_type]); + } + if (!feature_name.empty()) + target_xml += " </feature>\n"; target_xml += "</target>\n"; m_target_xml = escape_packet(target_xml); } @@ -746,47 +958,48 @@ void debug_gdbstub::wait_for_debugger(device_t &device, bool firststop) const gdb_register_map ®ister_map = it->second; m_gdb_arch = register_map.arch; - m_gdb_feature = register_map.feature; int cur_gdb_regnum = 0; - for ( const auto ®: register_map.registers ) - { - bool added = false; - for ( const auto &entry: m_state->state_entries() ) + for ( const auto &feature: register_map.features ) + for ( const auto ®: feature.registers ) { - const char *symbol = entry->symbol(); - if ( strcmp(symbol, reg.state_name) == 0 ) + bool added = false; + for ( const auto &entry: m_state->state_entries() ) { - gdb_register new_reg; - new_reg.gdb_name = reg.gdb_name; - new_reg.gdb_regnum = cur_gdb_regnum; - new_reg.gdb_type = reg.gdb_type; - if ( reg.override_bitsize != -1 ) - new_reg.gdb_bitsize = reg.override_bitsize; - else - new_reg.gdb_bitsize = entry->datasize() * 8; - new_reg.state_index = entry->index(); - m_gdb_registers.push_back(std::move(new_reg)); - if ( reg.stop_packet ) - m_stop_reply_registers.insert(cur_gdb_regnum); - added = true; - cur_gdb_regnum++; - break; + const char *symbol = entry->symbol(); + if ( strcmp(symbol, reg.state_name) == 0 ) + { + gdb_register new_reg; + new_reg.gdb_feature_name = feature.feature_name; + new_reg.gdb_name = reg.gdb_name; + new_reg.gdb_regnum = cur_gdb_regnum; + new_reg.gdb_type = reg.gdb_type; + if ( reg.override_bitsize != -1 ) + new_reg.gdb_bitsize = reg.override_bitsize; + else + new_reg.gdb_bitsize = entry->datasize() * 8; + new_reg.state_index = entry->index(); + m_gdb_registers.push_back(std::move(new_reg)); + if ( reg.stop_packet ) + m_stop_reply_registers.insert(cur_gdb_regnum); + added = true; + cur_gdb_regnum++; + break; + } } + if ( !added ) + osd_printf_info("gdbstub: could not find register [%s]\n", reg.gdb_name); } - if ( !added ) - osd_printf_info("gdbstub: could not find register [%s]\n", reg.gdb_name); - } #if 0 for ( const auto ®: m_gdb_registers ) osd_printf_info(" %3d (%d) %d %d [%s]\n", reg.gdb_regnum, reg.state_index, reg.gdb_bitsize, reg.gdb_type, reg.gdb_name); #endif - std::string socket_name = string_format("socket.localhost:%d", m_debugger_port); + std::string socket_name = string_format("socket.%s:%d", m_debugger_host, m_debugger_port); std::error_condition const filerr = m_socket.open(socket_name); if ( filerr ) - fatalerror("gdbstub: failed to start listening on port %d\n", m_debugger_port); - osd_printf_info("gdbstub: listening on port %d\n", m_debugger_port); + fatalerror("gdbstub: failed to start listening on address %s port %d\n", m_debugger_host, m_debugger_port); + osd_printf_info("gdbstub: listening on address %s port %d\n", m_debugger_host, m_debugger_port); m_initialized = true; } @@ -832,26 +1045,24 @@ void debug_gdbstub::debugger_update() //------------------------------------------------------------------------- void debug_gdbstub::send_nack() { - m_socket.puts("-"); + m_socket.write("-", 1); } //------------------------------------------------------------------------- void debug_gdbstub::send_ack() { - m_socket.puts("+"); + m_socket.write("+", 1); } //------------------------------------------------------------------------- -void debug_gdbstub::send_reply(const char *str) +void debug_gdbstub::send_reply(std::string_view str) { - size_t length = strlen(str); - uint8_t checksum = 0; - for ( size_t i = 0; i < length; i++ ) - checksum += str[i]; + for ( char ch : str ) + checksum += ch; std::string reply = string_format("$%s#%02x", str, checksum); - m_socket.puts(reply); + m_socket.write(reply.c_str(), reply.length()); } @@ -909,7 +1120,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_g(const char *buf) std::string reply; for ( const auto ®: m_gdb_registers ) reply += get_register_string(reg.gdb_regnum); - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } @@ -978,7 +1189,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_m(const char *buf) uint8_t value = tspace->read_byte(offset + i); reply += string_format("%02x", value); } - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } @@ -1034,7 +1245,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_p(const char *buf) if ( sscanf(buf, "%x", &gdb_regnum) != 1 || gdb_regnum >= m_gdb_registers.size() ) return REPLY_ENN; std::string reply = get_register_string(gdb_regnum); - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } @@ -1101,7 +1312,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_q(const char *buf) reply += string_format("%02x", *line++); reply += "0A"; } - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } @@ -1118,7 +1329,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_q(const char *buf) { std::string reply = string_format("PacketSize=%x", MAX_PACKET_SIZE); reply += ";qXfer:features:read+"; - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } else if ( name == "Xfer" ) @@ -1139,7 +1350,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_q(const char *buf) else reply += 'l'; reply += m_target_xml.substr(offset, length); - send_reply(reply.c_str()); + send_reply(reply); m_target_xml_sent = true; return REPLY_NONE; } @@ -1326,7 +1537,7 @@ void debug_gdbstub::send_stop_packet() if ( m_target_xml_sent ) for ( const auto &gdb_regnum: m_stop_reply_registers ) reply += string_format("%02x:%s;", gdb_regnum, get_register_string(gdb_regnum)); - send_reply(reply.c_str()); + send_reply(reply); } //------------------------------------------------------------------------- diff --git a/src/osd/modules/debugger/debugimgui.cpp b/src/osd/modules/debugger/debugimgui.cpp index 0f6bdd1f301..e027cb5339c 100644 --- a/src/osd/modules/debugger/debugimgui.cpp +++ b/src/osd/modules/debugger/debugimgui.cpp @@ -18,6 +18,7 @@ #include "debug/debugcpu.h" #include "debugger.h" #include "render.h" +#include "ui/uimain.h" #include "uiinput.h" #include "formats/flopimg.h" @@ -36,16 +37,16 @@ class debug_area DISABLE_COPYING(debug_area); public: - debug_area(running_machine &machine, debug_view_type type) - : next(nullptr), - type(0), - ofs_x(0), - ofs_y(0), - is_collapsed(false), - exec_cmd(false), - scroll_end(false), - scroll_follow(false) - { + debug_area(running_machine &machine, debug_view_type type) : + next(nullptr), + type(0), + ofs_x(0), + ofs_y(0), + is_collapsed(false), + exec_cmd(false), + scroll_end(false), + scroll_follow(false) + { this->view = machine.debug_view().alloc_view(type, nullptr, this); this->type = type; this->m_machine = &machine; @@ -63,7 +64,7 @@ public: default: break; } - } + } ~debug_area() { //this->target->debug_free(*this->container); @@ -72,7 +73,7 @@ public: running_machine &machine() const { assert(m_machine != nullptr); return *m_machine; } - debug_area * next; + debug_area * next; int type; debug_view * view; @@ -99,9 +100,11 @@ public: class debug_imgui : public osd_module, public debug_module { public: - debug_imgui() - : osd_module(OSD_DEBUG_PROVIDER, "imgui"), debug_module(), + debug_imgui() : + osd_module(OSD_DEBUG_PROVIDER, "imgui"), debug_module(), m_machine(nullptr), + m_take_ui(false), + m_current_pointer(-1), m_mouse_x(0), m_mouse_y(0), m_mouse_button(false), @@ -155,9 +158,8 @@ private: std::string longname; }; - void handle_mouse(); + void handle_events(); void handle_mouse_views(); - void handle_keys(); void handle_keys_views(); void handle_console(running_machine* machine); void update(); @@ -180,19 +182,21 @@ private: void refresh_filelist(); void refresh_typelist(); void update_cpu_view(device_t* device); - static bool get_view_source(void* data, int idx, const char** out_text); + static const char* get_view_source(void* data, int idx); static int history_set(ImGuiInputTextCallbackData* data); running_machine* m_machine; - int32_t m_mouse_x; - int32_t m_mouse_y; + bool m_take_ui; + int32_t m_current_pointer; + int32_t m_mouse_x; + int32_t m_mouse_y; bool m_mouse_button; bool m_prev_mouse_button; bool m_running; const char* font_name; float font_size; ImVec2 m_text_size; // size of character (assumes monospaced font is in use) - uint8_t m_key_char; + uint8_t m_key_char; bool m_hide; int m_win_count; // number of active windows, does not decrease, used to ID individual windows bool m_has_images; // true if current system has any image devices @@ -264,112 +268,92 @@ static inline void map_attr_to_fg_bg(unsigned char attr, rgb_t *fg, rgb_t *bg) } } -bool debug_imgui::get_view_source(void* data, int idx, const char** out_text) +const char* debug_imgui::get_view_source(void* data, int idx) { auto* vw = static_cast<debug_view*>(data); - *out_text = vw->source(idx)->name(); - return true; + return vw->source(idx)->name(); } -void debug_imgui::handle_mouse() +void debug_imgui::handle_events() { - m_prev_mouse_button = m_mouse_button; - m_machine->ui_input().find_mouse(&m_mouse_x, &m_mouse_y, &m_mouse_button); ImGuiIO& io = ImGui::GetIO(); - io.MousePos = ImVec2(m_mouse_x,m_mouse_y); - io.MouseDown[0] = m_mouse_button; -} - -void debug_imgui::handle_mouse_views() -{ - rectangle rect; - bool clicked = false; - if(m_mouse_button == true && m_prev_mouse_button == false) - clicked = true; - - // check all views, and pass mouse clicks to them - if(!m_mouse_button) - return; - rect.min_x = view_main_disasm->ofs_x; - rect.min_y = view_main_disasm->ofs_y; - rect.max_x = view_main_disasm->ofs_x + view_main_disasm->view_width; - rect.max_y = view_main_disasm->ofs_y + view_main_disasm->view_height; - if(rect.contains(m_mouse_x,m_mouse_y) && clicked && view_main_disasm->has_focus) - { - debug_view_xy topleft = view_main_disasm->view->visible_position(); - debug_view_xy newpos; - newpos.x = topleft.x + (m_mouse_x-view_main_disasm->ofs_x) / m_text_size.x; - newpos.y = topleft.y + (m_mouse_y-view_main_disasm->ofs_y) / m_text_size.y; - view_main_disasm->view->set_cursor_position(newpos); - view_main_disasm->view->set_cursor_visible(true); - } - for(auto it = view_list.begin();it != view_list.end();++it) - { - rect.min_x = (*it)->ofs_x; - rect.min_y = (*it)->ofs_y; - rect.max_x = (*it)->ofs_x + (*it)->view_width; - rect.max_y = (*it)->ofs_y + (*it)->view_height; - if(rect.contains(m_mouse_x,m_mouse_y) && clicked && (*it)->has_focus) - { - if((*it)->view->cursor_supported()) - { - debug_view_xy topleft = (*it)->view->visible_position(); - debug_view_xy newpos; - newpos.x = topleft.x + (m_mouse_x-(*it)->ofs_x) / m_text_size.x; - newpos.y = topleft.y + (m_mouse_y-(*it)->ofs_y) / m_text_size.y; - (*it)->view->set_cursor_position(newpos); - (*it)->view->set_cursor_visible(true); - } - } - } -} - -void debug_imgui::handle_keys() -{ - ImGuiIO& io = ImGui::GetIO(); - ui_event event; - debug_area* focus_view = nullptr; // find view that has focus (should only be one at a time) - for(auto view_ptr = view_list.begin();view_ptr != view_list.end();++view_ptr) + debug_area* focus_view = nullptr; + for(auto view_ptr = view_list.begin();view_ptr != view_list.end(); ++view_ptr) if((*view_ptr)->has_focus) focus_view = *view_ptr; // check views in main views also (only the disassembler view accepts inputs) - if(view_main_disasm != nullptr) + if(view_main_disasm) if(view_main_disasm->has_focus) focus_view = view_main_disasm; - if(m_machine->input().code_pressed(KEYCODE_LCONTROL)) - io.KeyCtrl = true; - else - io.KeyCtrl = false; - if(m_machine->input().code_pressed(KEYCODE_LSHIFT)) - io.KeyShift = true; - else - io.KeyShift = false; - if(m_machine->input().code_pressed(KEYCODE_LALT)) - io.KeyAlt = true; - else - io.KeyAlt = false; + io.KeyCtrl = m_machine->input().code_pressed(KEYCODE_LCONTROL); + io.KeyShift = m_machine->input().code_pressed(KEYCODE_LSHIFT); + io.KeyAlt = m_machine->input().code_pressed(KEYCODE_LALT); for(input_item_id id = ITEM_ID_A; id <= ITEM_ID_CANCEL; ++id) { - if(m_machine->input().code_pressed(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id))) { - if (m_mapping.count(id)) io.AddKeyEvent(m_mapping[id], true); - } else { - if (m_mapping.count(id)) io.AddKeyEvent(m_mapping[id], false); + if(m_machine->input().code_pressed(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id))) + { + if(m_mapping.count(id)) + io.AddKeyEvent(m_mapping[id], true); + } + else + { + if(m_mapping.count(id)) + io.AddKeyEvent(m_mapping[id], false); } } + m_prev_mouse_button = m_mouse_button; m_key_char = 0; - while (m_machine->ui_input().pop_event(&event)) + ui_event event; + while(m_machine->ui_input().pop_event(&event)) { switch (event.event_type) { + case ui_event::type::POINTER_UPDATE: + if(&m_machine->render().ui_target() != event.target) + break; + if(event.pointer_id != m_current_pointer) + { + if((0 > m_current_pointer) || ((event.pointer_pressed & 1) && !m_mouse_button)) + m_current_pointer = event.pointer_id; + } + if(event.pointer_id == m_current_pointer) + { + bool changed = (m_mouse_x != event.pointer_x) || (m_mouse_y != event.pointer_y) || (m_mouse_button != bool(event.pointer_buttons & 1)); + m_mouse_x = event.pointer_x; + m_mouse_y = event.pointer_y; + m_mouse_button = bool(event.pointer_buttons & 1); + if(changed) + { + io.MousePos = ImVec2(m_mouse_x,m_mouse_y); + io.MouseDown[0] = m_mouse_button; + } + } + break; + case ui_event::type::POINTER_LEAVE: + case ui_event::type::POINTER_ABORT: + if((&m_machine->render().ui_target() == event.target) && (event.pointer_id == m_current_pointer)) + { + m_current_pointer = -1; + bool changed = (m_mouse_x != event.pointer_x) || (m_mouse_y != event.pointer_y) || m_mouse_button; + m_mouse_x = event.pointer_x; + m_mouse_y = event.pointer_y; + m_mouse_button = false; + if(changed) + { + io.MousePos = ImVec2(m_mouse_x,m_mouse_y); + io.MouseDown[0] = m_mouse_button; + } + } + break; case ui_event::type::IME_CHAR: - m_key_char = event.ch; - if(focus_view != nullptr) + m_key_char = event.ch; // FIXME: assigning 4-byte UCS4 character to 8-bit variable + if(focus_view) focus_view->view->process_char(m_key_char); return; default: @@ -418,19 +402,63 @@ void debug_imgui::handle_keys() m_hide = true; } - if(ImGui::IsKeyPressed(ImGuiKey_D,false) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyPressed(ImGuiKey_D,false) && io.KeyCtrl) add_disasm(++m_win_count); - if(ImGui::IsKeyPressed(ImGuiKey_M,false) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyPressed(ImGuiKey_M,false) && io.KeyCtrl) add_memory(++m_win_count); - if(ImGui::IsKeyPressed(ImGuiKey_B,false) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyPressed(ImGuiKey_B,false) && io.KeyCtrl) add_bpoints(++m_win_count); - if(ImGui::IsKeyPressed(ImGuiKey_W,false) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyPressed(ImGuiKey_W,false) && io.KeyCtrl) add_wpoints(++m_win_count); - if(ImGui::IsKeyPressed(ImGuiKey_L,false) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) + if(ImGui::IsKeyPressed(ImGuiKey_L,false) && io.KeyCtrl) add_log(++m_win_count); } +void debug_imgui::handle_mouse_views() +{ + rectangle rect; + bool clicked = false; + if(m_mouse_button == true && m_prev_mouse_button == false) + clicked = true; + + // check all views, and pass mouse clicks to them + if(!m_mouse_button) + return; + rect.min_x = view_main_disasm->ofs_x; + rect.min_y = view_main_disasm->ofs_y; + rect.max_x = view_main_disasm->ofs_x + view_main_disasm->view_width; + rect.max_y = view_main_disasm->ofs_y + view_main_disasm->view_height; + if(rect.contains(m_mouse_x,m_mouse_y) && clicked && view_main_disasm->has_focus) + { + debug_view_xy topleft = view_main_disasm->view->visible_position(); + debug_view_xy newpos; + newpos.x = topleft.x + (m_mouse_x-view_main_disasm->ofs_x) / m_text_size.x; + newpos.y = topleft.y + (m_mouse_y-view_main_disasm->ofs_y) / m_text_size.y; + view_main_disasm->view->set_cursor_position(newpos); + view_main_disasm->view->set_cursor_visible(true); + } + for(auto it = view_list.begin();it != view_list.end();++it) + { + rect.min_x = (*it)->ofs_x; + rect.min_y = (*it)->ofs_y; + rect.max_x = (*it)->ofs_x + (*it)->view_width; + rect.max_y = (*it)->ofs_y + (*it)->view_height; + if(rect.contains(m_mouse_x,m_mouse_y) && clicked && (*it)->has_focus) + { + if((*it)->view->cursor_supported()) + { + debug_view_xy topleft = (*it)->view->visible_position(); + debug_view_xy newpos; + newpos.x = topleft.x + (m_mouse_x-(*it)->ofs_x) / m_text_size.x; + newpos.y = topleft.y + (m_mouse_y-(*it)->ofs_y) / m_text_size.y; + (*it)->view->set_cursor_position(newpos); + (*it)->view->set_cursor_visible(true); + } + } + } +} + void debug_imgui::handle_keys_views() { debug_area* focus_view = nullptr; @@ -618,7 +646,10 @@ void debug_imgui::draw_view(debug_area* view_ptr, bool exp_change) // temporarily set cursor to the last line, this will set the scroll bar range if(view_ptr->type != DVT_MEMORY) // no scroll bars in memory views + { ImGui::SetCursorPosY((totalsize.y) * fsize.y); + ImGui::Dummy(ImVec2(0,0)); // some object is required for validation + } // set the visible area to be displayed vsize.x = view_ptr->view_width / fsize.x; @@ -1550,22 +1581,31 @@ void debug_imgui::wait_for_debugger(device_t &device, bool firststop) } if(firststop) { -// debug_show_all(); - device.machine().ui_input().reset(); + //debug_show_all(); m_running = false; } + if(!m_take_ui) + { + if (!m_machine->ui().set_ui_event_handler([this] () { return m_take_ui; })) + { + // can't break if we can't take over UI input + m_machine->debugger().console().get_visible_cpu()->debug()->go(); + m_running = true; + return; + } + m_take_ui = true; + + } m_hide = false; - //m_machine->ui_input().frame_update(); - handle_mouse(); - handle_keys(); + m_machine->osd().input_update(false); + handle_events(); handle_console(m_machine); update_cpu_view(&device); - imguiBeginFrame(m_mouse_x,m_mouse_y,m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height,m_key_char); + imguiBeginFrame(m_mouse_x, m_mouse_y, m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height,m_key_char); handle_mouse_views(); handle_keys_views(); update(); imguiEndFrame(); - m_machine->ui_input().reset(); // clear remaining inputs, so they don't fall through to the UI device.machine().osd().update(false); osd_sleep(osd_ticks_per_second() / 1000 * 50); } @@ -1573,18 +1613,31 @@ void debug_imgui::wait_for_debugger(device_t &device, bool firststop) void debug_imgui::debugger_update() { - if(view_main_disasm == nullptr || view_main_regs == nullptr || view_main_console == nullptr) + if(!view_main_disasm || !view_main_regs || !view_main_console || !m_machine || (m_machine->phase() != machine_phase::RUNNING)) return; - if (m_machine && (m_machine->phase() == machine_phase::RUNNING) && !m_machine->debugger().cpu().is_stopped() && !m_hide) + if(!m_machine->debugger().cpu().is_stopped()) { - uint32_t width = m_machine->render().ui_target().width(); - uint32_t height = m_machine->render().ui_target().height(); - handle_mouse(); - handle_keys(); - imguiBeginFrame(m_mouse_x,m_mouse_y,m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height, m_key_char); - update(); - imguiEndFrame(); + if(m_take_ui) + { + m_take_ui = false; + m_current_pointer = -1; + m_prev_mouse_button = m_mouse_button; + if(m_mouse_button) + { + m_mouse_button = false; + ImGuiIO& io = ImGui::GetIO(); + io.MouseDown[0] = false; + } + } + if(!m_hide) + { + uint32_t width = m_machine->render().ui_target().width(); + uint32_t height = m_machine->render().ui_target().height(); + imguiBeginFrame(m_mouse_x, m_mouse_y, 0, 0, width, height, m_key_char); + update(); + imguiEndFrame(); + } } } diff --git a/src/osd/modules/debugger/debugqt.cpp b/src/osd/modules/debugger/debugqt.cpp index cdf3fb014bc..aeae07324e4 100644 --- a/src/osd/modules/debugger/debugqt.cpp +++ b/src/osd/modules/debugger/debugqt.cpp @@ -181,7 +181,7 @@ void debug_qt::wait_for_debugger(device_t &device, bool firststop) void debug_qt::debugger_update() { - qApp->processEvents(QEventLoop::AllEvents, 1); + qApp->processEvents(QEventLoop::AllEvents); } diff --git a/src/osd/modules/debugger/debugwin.cpp b/src/osd/modules/debugger/debugwin.cpp index 43d20ae371d..8ff26d40fb3 100644 --- a/src/osd/modules/debugger/debugwin.cpp +++ b/src/osd/modules/debugger/debugwin.cpp @@ -56,7 +56,9 @@ public: m_main_console(nullptr), m_next_window_pos{ 0, 0 }, m_config(), - m_save_windows(true) + m_save_windows(true), + m_group_windows(true), + m_group_windows_setting(true) { } @@ -76,6 +78,9 @@ protected: virtual void set_color_theme(int index) override; virtual bool get_save_window_arrangement() const override { return m_save_windows; } virtual void set_save_window_arrangement(bool save) override { m_save_windows = save; } + virtual bool get_group_windows() const override { return m_group_windows; } + virtual bool get_group_windows_setting() const override { return m_group_windows_setting; } + virtual void set_group_windows_setting(bool group) override { m_group_windows_setting = group; } virtual bool const &waiting_for_debugger() const override { return m_waiting_for_debugger; } virtual bool seq_pressed() const override; @@ -111,6 +116,8 @@ private: util::xml::file::ptr m_config; bool m_save_windows; + bool m_group_windows; + bool m_group_windows_setting; }; @@ -214,7 +221,7 @@ void debugger_windows::wait_for_debugger(device_t &device, bool firststop) // process everything else default: - winwindow_dispatch_message(*m_machine, &message); + winwindow_dispatch_message(*m_machine, message); break; } @@ -395,6 +402,7 @@ void debugger_windows::config_load(config_type cfgtype, config_level cfglevel, u if (config_type::DEFAULT == cfgtype) { m_save_windows = 0 != parentnode->get_attribute_int(debugger::ATTR_DEBUGGER_SAVE_WINDOWS, m_save_windows ? 1 : 0); + m_group_windows = m_group_windows_setting = 0 != parentnode->get_attribute_int(debugger::ATTR_DEBUGGER_GROUP_WINDOWS, m_group_windows ? 1 : 0); util::xml::data_node const *const colors = parentnode->get_child(debugger::NODE_COLORS); if (colors) m_metrics->set_color_theme(colors->get_attribute_int(debugger::ATTR_COLORS_THEME, m_metrics->get_color_theme())); @@ -421,6 +429,7 @@ void debugger_windows::config_save(config_type cfgtype, util::xml::data_node *pa if (config_type::DEFAULT == cfgtype) { parentnode->set_attribute_int(debugger::ATTR_DEBUGGER_SAVE_WINDOWS, m_save_windows ? 1 : 0); + parentnode->set_attribute_int(debugger::ATTR_DEBUGGER_GROUP_WINDOWS, m_group_windows_setting ? 1 : 0); util::xml::data_node *const colors = parentnode->add_child(debugger::NODE_COLORS, nullptr); if (colors) colors->set_attribute_int(debugger::ATTR_COLORS_THEME, m_metrics->get_color_theme()); diff --git a/src/osd/modules/debugger/osx/debugview.mm b/src/osd/modules/debugger/osx/debugview.mm index ebf4ce257d5..504eaab8fe3 100644 --- a/src/osd/modules/debugger/osx/debugview.mm +++ b/src/osd/modules/debugger/osx/debugview.mm @@ -432,15 +432,15 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) NSRange const run = NSMakeRange(0, [text length]); [text addAttribute:NSFontAttributeName value:font range:run]; NSPasteboard *const board = [NSPasteboard generalPasteboard]; - [board declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil]; - [board setData:[text RTFFromRange:run documentAttributes:[NSDictionary dictionary]] forType:NSRTFPboardType]; + [board declareTypes:[NSArray arrayWithObject:NSPasteboardTypeRTF] owner:nil]; + [board setData:[text RTFFromRange:run documentAttributes:[NSDictionary dictionary]] forType:NSPasteboardTypeRTF]; [text deleteCharactersInRange:run]; } - (IBAction)paste:(id)sender { NSPasteboard *const board = [NSPasteboard generalPasteboard]; - NSString *const avail = [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; + NSString *const avail = [board availableTypeFromArray:[NSArray arrayWithObject:NSPasteboardTypeString]]; if (avail == nil) { NSBeep(); @@ -921,7 +921,7 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) if (action == @selector(paste:)) { NSPasteboard *const board = [NSPasteboard generalPasteboard]; - return [board availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]] != nil; + return [board availableTypeFromArray:[NSArray arrayWithObject:NSPasteboardTypeString]] != nil; } else { diff --git a/src/osd/modules/debugger/osx/disassemblyviewer.mm b/src/osd/modules/debugger/osx/disassemblyviewer.mm index 20ec457f628..a0659c00cfc 100644 --- a/src/osd/modules/debugger/osx/disassemblyviewer.mm +++ b/src/osd/modules/debugger/osx/disassemblyviewer.mm @@ -45,9 +45,7 @@ [expressionField sizeToFit]; // create the subview popup - subviewButton = [[NSPopUpButton alloc] initWithFrame:NSOffsetRect(expressionFrame, - expressionFrame.size.width, - 0)]; + subviewButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; [subviewButton setBezelStyle:NSBezelStyleShadowlessSquare]; [subviewButton setFocusRingType:NSFocusRingTypeNone]; diff --git a/src/osd/modules/debugger/osx/exceptionpointsview.h b/src/osd/modules/debugger/osx/exceptionpointsview.h new file mode 100644 index 00000000000..73e3958c9b8 --- /dev/null +++ b/src/osd/modules/debugger/osx/exceptionpointsview.h @@ -0,0 +1,23 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +//============================================================ +// +// exceptionpointsview.h - MacOS X Cocoa debug window handling +// +//============================================================ + +#import "debugosx.h" + +#import "debugview.h" + + +#import <Cocoa/Cocoa.h> + + +@interface MAMEExceptionpointsView : MAMEDebugView +{ +} + +- (id)initWithFrame:(NSRect)f machine:(running_machine &)m; + +@end diff --git a/src/osd/modules/debugger/osx/exceptionpointsview.mm b/src/osd/modules/debugger/osx/exceptionpointsview.mm new file mode 100644 index 00000000000..c15f9c56f4f --- /dev/null +++ b/src/osd/modules/debugger/osx/exceptionpointsview.mm @@ -0,0 +1,27 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +//============================================================ +// +// exceptionpointsview.m - MacOS X Cocoa debug window handling +// +//============================================================ + +#import "exceptionpointsview.h" + +#include "debug/debugvw.h" + + +@implementation MAMEExceptionpointsView + +- (id)initWithFrame:(NSRect)f machine:(running_machine &)m { + if (!(self = [super initWithFrame:f type:DVT_EXCEPTION_POINTS machine:m wholeLineScroll:YES])) + return nil; + return self; +} + + +- (void)dealloc { + [super dealloc]; +} + +@end diff --git a/src/osd/modules/debugger/osx/pointsviewer.mm b/src/osd/modules/debugger/osx/pointsviewer.mm index 8b2f69c50b5..0fafe35a799 100644 --- a/src/osd/modules/debugger/osx/pointsviewer.mm +++ b/src/osd/modules/debugger/osx/pointsviewer.mm @@ -10,6 +10,7 @@ #import "pointsviewer.h" #import "breakpointsview.h" +#import "exceptionpointsview.h" #import "registerpointsview.h" #import "watchpointsview.h" @@ -19,9 +20,9 @@ @implementation MAMEPointsViewer - (id)initWithMachine:(running_machine &)m console:(MAMEDebugConsole *)c { - MAMEDebugView *breakView, *watchView, *registerView; - NSScrollView *breakScroll, *watchScroll, *registerScroll; - NSTabViewItem *breakTab, *watchTab, *registerTab; + MAMEDebugView *breakView, *watchView, *registerView, *exceptionView; + NSScrollView *breakScroll, *watchScroll, *registerScroll, *exceptionScroll; + NSTabViewItem *breakTab, *watchTab, *registerTab, *exceptionTab; NSPopUpButton *actionButton; NSRect subviewFrame; @@ -48,6 +49,9 @@ [[[subviewButton menu] addItemWithTitle:@"All Registerpoints" action:NULL keyEquivalent:@""] setTag:2]; + [[[subviewButton menu] addItemWithTitle:@"All Exceptionpoints" + action:NULL + keyEquivalent:@""] setTag:3]; [subviewButton sizeToFit]; subviewFrame = [subviewButton frame]; subviewFrame.origin.x = subviewFrame.size.height; @@ -118,7 +122,23 @@ [registerTab setView:registerScroll]; [registerScroll release]; - // create a tabless tabview for the two subviews + // create the exceptionpoints view + exceptionView = [[MAMEExceptionpointsView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) + machine:*machine]; + exceptionScroll = [[NSScrollView alloc] initWithFrame:[breakScroll frame]]; + [exceptionScroll setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [exceptionScroll setHasHorizontalScroller:YES]; + [exceptionScroll setHasVerticalScroller:YES]; + [exceptionScroll setAutohidesScrollers:YES]; + [exceptionScroll setBorderType:NSNoBorder]; + [exceptionScroll setDrawsBackground:NO]; + [exceptionScroll setDocumentView:exceptionView]; + [exceptionView release]; + exceptionTab = [[NSTabViewItem alloc] initWithIdentifier:@""]; + [exceptionTab setView:exceptionScroll]; + [exceptionScroll release]; + + // create a tabless tabview for the four subviews tabs = [[NSTabView alloc] initWithFrame:[breakScroll frame]]; [tabs setTabViewType:NSNoTabsNoBorder]; [tabs setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; @@ -128,6 +148,8 @@ [watchTab release]; [tabs addTabViewItem:registerTab]; [registerTab release]; + [tabs addTabViewItem:exceptionTab]; + [exceptionTab release]; [[window contentView] addSubview:tabs]; [tabs release]; @@ -156,8 +178,14 @@ borderType:[registerScroll borderType] controlSize:NSControlSizeRegular scrollerStyle:NSScrollerStyleOverlay]; - NSSize const desired = NSMakeSize(std::max({ breakDesired.width, watchDesired.width, registerDesired.width }), - std::max({ breakDesired.height, watchDesired.height, registerDesired.height })); + NSSize const exceptionDesired = [NSScrollView frameSizeForContentSize:[exceptionView maximumFrameSize] + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[exceptionScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; + NSSize const desired = NSMakeSize(std::max({ breakDesired.width, watchDesired.width, registerDesired.width, exceptionDesired.width }), + std::max({ breakDesired.height, watchDesired.height, registerDesired.height, exceptionDesired.height })); [self cascadeWindowWithDesiredSize:desired forView:tabs]; // don't forget the result diff --git a/src/osd/modules/debugger/qt/breakpointswindow.cpp b/src/osd/modules/debugger/qt/breakpointswindow.cpp index 16c8af6bdb3..7dc3db34179 100644 --- a/src/osd/modules/debugger/qt/breakpointswindow.cpp +++ b/src/osd/modules/debugger/qt/breakpointswindow.cpp @@ -3,9 +3,6 @@ #include "emu.h" #include "breakpointswindow.h" -#include "debug/debugcon.h" -#include "debug/debugcpu.h" - #include "util/xmlfile.h" #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -73,6 +70,12 @@ BreakpointsWindow::BreakpointsWindow(DebuggerQt &debugger, QWidget *parent) : typeRegister->setActionGroup(typeGroup); typeRegister->setShortcut(QKeySequence("Ctrl+3")); + QAction *typeException = new QAction("Exceptionpoints", this); + typeException->setObjectName("typeexception"); + typeException->setCheckable(true); + typeException->setActionGroup(typeGroup); + typeException->setShortcut(QKeySequence("Ctrl+4")); + typeBreak->setChecked(true); connect(typeGroup, &QActionGroup::triggered, this, &BreakpointsWindow::typeChanged); @@ -119,6 +122,9 @@ void BreakpointsWindow::saveConfigurationToNode(util::xml::data_node &node) case DVT_REGISTER_POINTS: node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 2); break; + case DVT_EXCEPTION_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 3); + break; default: break; } @@ -150,6 +156,11 @@ void BreakpointsWindow::typeChanged(QAction* changedTo) m_breakpointsView = new DebuggerView(DVT_REGISTER_POINTS, m_machine, this); setWindowTitle("Debug: All Registerpoints"); } + else if (changedTo->text() == "Exceptionpoints") + { + m_breakpointsView = new DebuggerView(DVT_EXCEPTION_POINTS, m_machine, this); + setWindowTitle("Debug: All Exceptionpoints"); + } // Re-register QVBoxLayout *layout = findChild<QVBoxLayout *>("vlayout"); diff --git a/src/osd/modules/debugger/qt/dasmwindow.cpp b/src/osd/modules/debugger/qt/dasmwindow.cpp index 144e2f32f5d..e90054a3292 100644 --- a/src/osd/modules/debugger/qt/dasmwindow.cpp +++ b/src/osd/modules/debugger/qt/dasmwindow.cpp @@ -3,6 +3,7 @@ #include "emu.h" #include "dasmwindow.h" +#include "debugger.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debug/dvdisasm.h" diff --git a/src/osd/modules/debugger/qt/debuggerview.cpp b/src/osd/modules/debugger/qt/debuggerview.cpp index e50659fcf3a..6a184dcfcca 100644 --- a/src/osd/modules/debugger/qt/debuggerview.cpp +++ b/src/osd/modules/debugger/qt/debuggerview.cpp @@ -16,10 +16,6 @@ #include <QtWidgets/QApplication> #include <QtWidgets/QScrollBar> -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) -#define horizontalAdvance width -#endif - namespace osd::debugger::qt { @@ -312,8 +308,13 @@ void DebuggerView::mousePressEvent(QMouseEvent *event) debug_view_xy const topLeft = m_view->visible_position(); debug_view_xy const visibleCharDims = m_view->visible_size(); debug_view_xy clickViewPosition; - clickViewPosition.x = (std::min)(int(topLeft.x + (event->x() / fontWidth)), topLeft.x + visibleCharDims.x - 1); - clickViewPosition.y = (std::min)(int(topLeft.y + (event->y() / fontHeight)), topLeft.y + visibleCharDims.y - 1); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + const QPointF mousePosition = event->position(); +#else + const QPointF mousePosition = event->localPos(); +#endif + clickViewPosition.x = (std::min)(int(topLeft.x + (mousePosition.x() / fontWidth)), topLeft.x + visibleCharDims.x - 1); + clickViewPosition.y = (std::min)(int(topLeft.y + (mousePosition.y() / fontHeight)), topLeft.y + visibleCharDims.y - 1); if (event->button() == Qt::LeftButton) { diff --git a/src/osd/modules/debugger/qt/logwindow.cpp b/src/osd/modules/debugger/qt/logwindow.cpp index dfbe947caca..c3a6b442f0f 100644 --- a/src/osd/modules/debugger/qt/logwindow.cpp +++ b/src/osd/modules/debugger/qt/logwindow.cpp @@ -3,10 +3,6 @@ #include "emu.h" #include "logwindow.h" -#include "debug/debugcon.h" -#include "debug/debugcpu.h" -#include "debug/dvdisasm.h" - #include "util/xmlfile.h" #include <QtWidgets/QVBoxLayout> diff --git a/src/osd/modules/debugger/qt/mainwindow.cpp b/src/osd/modules/debugger/qt/mainwindow.cpp index dba5d18b9ac..0085dac364b 100644 --- a/src/osd/modules/debugger/qt/mainwindow.cpp +++ b/src/osd/modules/debugger/qt/mainwindow.cpp @@ -3,6 +3,7 @@ #include "emu.h" #include "mainwindow.h" +#include "debugger.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debug/dvdisasm.h" @@ -463,8 +464,9 @@ void MainWindow::debugActClose() void MainWindow::debuggerExit() { + // this isn't called from a Qt event loop, so close() will leak the window object m_exiting = true; - close(); + delete this; } diff --git a/src/osd/modules/debugger/qt/memorywindow.cpp b/src/osd/modules/debugger/qt/memorywindow.cpp index 030eb4ab24d..ec250055383 100644 --- a/src/osd/modules/debugger/qt/memorywindow.cpp +++ b/src/osd/modules/debugger/qt/memorywindow.cpp @@ -3,6 +3,7 @@ #include "emu.h" #include "memorywindow.h" +#include "debugger.h" #include "debug/dvmemory.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" @@ -25,10 +26,6 @@ #include <QtWidgets/QToolTip> #include <QtWidgets/QVBoxLayout> -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) -#define horizontalAdvance width -#endif - namespace osd::debugger::qt { diff --git a/src/osd/modules/debugger/qt/windowqt.cpp b/src/osd/modules/debugger/qt/windowqt.cpp index 783c5a24393..294871e6d22 100644 --- a/src/osd/modules/debugger/qt/windowqt.cpp +++ b/src/osd/modules/debugger/qt/windowqt.cpp @@ -9,6 +9,7 @@ #include "logwindow.h" #include "memorywindow.h" +#include "debugger.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" @@ -251,7 +252,8 @@ void WindowQt::debugActQuit() void WindowQt::debuggerExit() { - close(); + // this isn't called from a Qt event loop, so close() will leak the window object + delete this; } diff --git a/src/osd/modules/debugger/qt/windowqt.h b/src/osd/modules/debugger/qt/windowqt.h index 8085f9a63bf..b5e933e8a0c 100644 --- a/src/osd/modules/debugger/qt/windowqt.h +++ b/src/osd/modules/debugger/qt/windowqt.h @@ -5,8 +5,6 @@ #include "../xmlconfig.h" -#include "debugger.h" - #include <QtWidgets/QMainWindow> #include <deque> diff --git a/src/osd/modules/debugger/win/consolewininfo.cpp b/src/osd/modules/debugger/win/consolewininfo.cpp index 3a4a52be2e2..a5dfd04cc55 100644 --- a/src/osd/modules/debugger/win/consolewininfo.cpp +++ b/src/osd/modules/debugger/win/consolewininfo.cpp @@ -245,6 +245,7 @@ consolewin_info::consolewin_info(debugger_windows_interface &debugger) : // add the settings menu HMENU const settingsmenu = CreatePopupMenu(); AppendMenu(settingsmenu, MF_ENABLED, ID_SAVE_WINDOWS, TEXT("Save Window Arrangement")); + AppendMenu(settingsmenu, MF_ENABLED, ID_GROUP_WINDOWS, TEXT("Group Debugger Windows (requires restart)")); AppendMenu(settingsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(settingsmenu, MF_ENABLED, ID_LIGHT_BACKGROUND, TEXT("Light Background")); AppendMenu(settingsmenu, MF_ENABLED, ID_DARK_BACKGROUND, TEXT("Dark Background")); @@ -449,6 +450,7 @@ void consolewin_info::update_menu() HMENU const menu = GetMenu(window()); CheckMenuItem(menu, ID_SAVE_WINDOWS, MF_BYCOMMAND | (debugger().get_save_window_arrangement() ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(menu, ID_GROUP_WINDOWS, MF_BYCOMMAND | (debugger().get_group_windows_setting() ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(menu, ID_LIGHT_BACKGROUND, MF_BYCOMMAND | ((ui_metrics::THEME_LIGHT_BACKGROUND == metrics().get_color_theme()) ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(menu, ID_DARK_BACKGROUND, MF_BYCOMMAND | ((ui_metrics::THEME_DARK_BACKGROUND == metrics().get_color_theme()) ? MF_CHECKED : MF_UNCHECKED)); } @@ -518,6 +520,9 @@ bool consolewin_info::handle_command(WPARAM wparam, LPARAM lparam) case ID_SAVE_WINDOWS: debugger().set_save_window_arrangement(!debugger().get_save_window_arrangement()); return true; + case ID_GROUP_WINDOWS: + debugger().set_group_windows_setting(!debugger().get_group_windows_setting()); + return true; case ID_LIGHT_BACKGROUND: debugger().set_color_theme(ui_metrics::THEME_LIGHT_BACKGROUND); return true; diff --git a/src/osd/modules/debugger/win/debugwin.h b/src/osd/modules/debugger/win/debugwin.h index 52303eefc2c..d8af29b795e 100644 --- a/src/osd/modules/debugger/win/debugwin.h +++ b/src/osd/modules/debugger/win/debugwin.h @@ -40,6 +40,9 @@ public: virtual void set_color_theme(int index) = 0; virtual bool get_save_window_arrangement() const = 0; virtual void set_save_window_arrangement(bool save) = 0; + virtual bool get_group_windows() const = 0; + virtual bool get_group_windows_setting() const = 0; + virtual void set_group_windows_setting(bool group) = 0; virtual bool const &waiting_for_debugger() const = 0; virtual bool seq_pressed() const = 0; diff --git a/src/osd/modules/debugger/win/debugwininfo.cpp b/src/osd/modules/debugger/win/debugwininfo.cpp index 8c3275a7549..f27f7933f08 100644 --- a/src/osd/modules/debugger/win/debugwininfo.cpp +++ b/src/osd/modules/debugger/win/debugwininfo.cpp @@ -47,7 +47,9 @@ debugwin_info::debugwin_info(debugger_windows_interface &debugger, bool is_main_ m_wnd = win_create_window_ex_utf8( DEBUG_WINDOW_STYLE_EX, "MAMEDebugWindow", title, DEBUG_WINDOW_STYLE, 0, 0, 100, 100, - dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(), + debugger.get_group_windows() + ? dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window() + : nullptr, create_standard_menubar(), GetModuleHandleUni(), this); @@ -559,8 +561,7 @@ LRESULT debugwin_info::window_proc(UINT message, WPARAM wparam, LPARAM lparam) auto *minmax = (MINMAXINFO *)lparam; minmax->ptMinTrackSize.x = m_minwidth; minmax->ptMinTrackSize.y = m_minheight; - minmax->ptMaxSize.x = minmax->ptMaxTrackSize.x = m_maxwidth; - minmax->ptMaxSize.y = minmax->ptMaxTrackSize.y = m_maxheight; + // Leave default ptMaxSize and ptMaxTrackSize so maximum size is not restricted break; } diff --git a/src/osd/modules/debugger/win/debugwininfo.h b/src/osd/modules/debugger/win/debugwininfo.h index f6f37c86955..cf3587253f6 100644 --- a/src/osd/modules/debugger/win/debugwininfo.h +++ b/src/osd/modules/debugger/win/debugwininfo.h @@ -110,10 +110,12 @@ protected: ID_SHOW_BREAKPOINTS, ID_SHOW_WATCHPOINTS, ID_SHOW_REGISTERPOINTS, + ID_SHOW_EXCEPTIONPOINTS, ID_CLEAR_LOG, ID_SAVE_WINDOWS, + ID_GROUP_WINDOWS, ID_LIGHT_BACKGROUND, ID_DARK_BACKGROUND, diff --git a/src/osd/modules/debugger/win/pointswininfo.cpp b/src/osd/modules/debugger/win/pointswininfo.cpp index f8fb4f6ce8d..70ec6606280 100644 --- a/src/osd/modules/debugger/win/pointswininfo.cpp +++ b/src/osd/modules/debugger/win/pointswininfo.cpp @@ -36,6 +36,7 @@ pointswin_info::pointswin_info(debugger_windows_interface &debugger) : AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_BREAKPOINTS, TEXT("Breakpoints\tCtrl+1")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_WATCHPOINTS, TEXT("Watchpoints\tCtrl+2")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_REGISTERPOINTS, TEXT("Registerpoints\tCtrl+3")); + AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_EXCEPTIONPOINTS, TEXT("Exceptionpoints\tCtrl+4")); AppendMenu(GetMenu(window()), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); // compute a client rect @@ -76,6 +77,10 @@ bool pointswin_info::handle_key(WPARAM wparam, LPARAM lparam) case '3': SendMessage(window(), WM_COMMAND, ID_SHOW_REGISTERPOINTS, 0); return true; + + case '4': + SendMessage(window(), WM_COMMAND, ID_SHOW_EXCEPTIONPOINTS, 0); + return true; } } @@ -91,6 +96,7 @@ void pointswin_info::update_menu() CheckMenuItem(menu, ID_SHOW_BREAKPOINTS, MF_BYCOMMAND | (m_views[0]->type() == DVT_BREAK_POINTS ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(menu, ID_SHOW_WATCHPOINTS, MF_BYCOMMAND | (m_views[0]->type() == DVT_WATCH_POINTS ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(menu, ID_SHOW_REGISTERPOINTS, MF_BYCOMMAND | (m_views[0]->type() == DVT_REGISTER_POINTS ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(menu, ID_SHOW_EXCEPTIONPOINTS, MF_BYCOMMAND | (m_views[0]->type() == DVT_EXCEPTION_POINTS ? MF_CHECKED : MF_UNCHECKED)); } @@ -128,6 +134,15 @@ bool pointswin_info::handle_command(WPARAM wparam, LPARAM lparam) win_set_window_text_utf8(window(), "All Registerpoints"); recompute_children(); return true; + + case ID_SHOW_EXCEPTIONPOINTS: + m_views[0].reset(); + m_views[0].reset(new debugview_info(debugger(), *this, window(), DVT_EXCEPTION_POINTS)); + if (!m_views[0]->is_valid()) + m_views[0].reset(); + win_set_window_text_utf8(window(), "All Exceptionpoints"); + recompute_children(); + return true; } break; } @@ -148,6 +163,9 @@ void pointswin_info::restore_configuration_from_node(util::xml::data_node const case 2: SendMessage(window(), WM_COMMAND, ID_SHOW_REGISTERPOINTS, 0); break; + case 3: + SendMessage(window(), WM_COMMAND, ID_SHOW_EXCEPTIONPOINTS, 0); + break; } debugwin_info::restore_configuration_from_node(node); @@ -170,6 +188,9 @@ void pointswin_info::save_configuration_to_node(util::xml::data_node &node) case DVT_REGISTER_POINTS: node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 2); break; + case DVT_EXCEPTION_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 3); + break; default: break; } diff --git a/src/osd/modules/debugger/xmlconfig.cpp b/src/osd/modules/debugger/xmlconfig.cpp index 7e702938bf8..977e7b55e70 100644 --- a/src/osd/modules/debugger/xmlconfig.cpp +++ b/src/osd/modules/debugger/xmlconfig.cpp @@ -17,6 +17,7 @@ char const *const NODE_WINDOW_HISTORY = "history"; char const *const NODE_HISTORY_ITEM = "item"; char const *const ATTR_DEBUGGER_SAVE_WINDOWS = "savewindows"; +char const *const ATTR_DEBUGGER_GROUP_WINDOWS = "groupwindows"; char const *const ATTR_WINDOW_TYPE = "type"; char const *const ATTR_WINDOW_POSITION_X = "position_x"; diff --git a/src/osd/modules/debugger/xmlconfig.h b/src/osd/modules/debugger/xmlconfig.h index ae5cdf8393c..6ae476cdec7 100644 --- a/src/osd/modules/debugger/xmlconfig.h +++ b/src/osd/modules/debugger/xmlconfig.h @@ -33,6 +33,7 @@ extern char const *const NODE_WINDOW_HISTORY; extern char const *const NODE_HISTORY_ITEM; extern char const *const ATTR_DEBUGGER_SAVE_WINDOWS; +extern char const *const ATTR_DEBUGGER_GROUP_WINDOWS; extern char const *const ATTR_WINDOW_TYPE; extern char const *const ATTR_WINDOW_POSITION_X; diff --git a/src/osd/modules/file/posixfile.cpp b/src/osd/modules/file/posixfile.cpp index c5ce8bf7b4e..8aebde2adc2 100644 --- a/src/osd/modules/file/posixfile.cpp +++ b/src/osd/modules/file/posixfile.cpp @@ -380,13 +380,17 @@ std::unique_ptr<osd::directory::entry> osd_stat(const std::string &path) // create an osd_directory_entry; be sure to make sure that the caller can // free all resources by just freeing the resulting osd_directory_entry - osd::directory::entry *result; - try { result = reinterpret_cast<osd::directory::entry *>(::operator new(sizeof(*result) + path.length() + 1)); } - catch (...) { return nullptr; } + auto const result = reinterpret_cast<osd::directory::entry *>( + ::operator new( + sizeof(osd::directory::entry) + path.length() + 1, + std::align_val_t(alignof(osd::directory::entry)), + std::nothrow)); + if (!result) return nullptr; new (result) osd::directory::entry; - std::strcpy(reinterpret_cast<char *>(result) + sizeof(*result), path.c_str()); - result->name = reinterpret_cast<char *>(result) + sizeof(*result); + auto const resultname = reinterpret_cast<char *>(result) + sizeof(*result); + std::strcpy(resultname, path.c_str()); + result->name = resultname; result->type = S_ISDIR(st.st_mode) ? osd::directory::entry::entry_type::DIR : osd::directory::entry::entry_type::FILE; result->size = std::uint64_t(std::make_unsigned_t<decltype(st.st_size)>(st.st_size)); result->last_modified = std::chrono::system_clock::from_time_t(st.st_mtime); diff --git a/src/osd/modules/font/font_sdl.cpp b/src/osd/modules/font/font_sdl.cpp index 1938c97a44d..514e0cc0784 100644 --- a/src/osd/modules/font/font_sdl.cpp +++ b/src/osd/modules/font/font_sdl.cpp @@ -15,11 +15,8 @@ #include "unicode.h" #include "osdcore.h" -#ifdef SDLMAME_EMSCRIPTEN -#include <SDL_ttf.h> -#else #include <SDL2/SDL_ttf.h> -#endif + #if !defined(SDLMAME_HAIKU) && !defined(SDLMAME_EMSCRIPTEN) #include <fontconfig/fontconfig.h> #endif diff --git a/src/osd/modules/input/input_common.cpp b/src/osd/modules/input/input_common.cpp index ec9521ab4b8..f3de3ca186e 100644 --- a/src/osd/modules/input/input_common.cpp +++ b/src/osd/modules/input/input_common.cpp @@ -20,7 +20,9 @@ #if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include <windows.h> -#define KEY_TRANS_WIN32(disc, virtual) KEY_##disc, virtual, +#include <dinput.h> +#define DIK_UNKNOWN 0xffff // intentionally impossible +#define KEY_TRANS_WIN32(disc, virtual) DIK_##disc, virtual, #else #define KEY_TRANS_WIN32(disc, virtual) #endif @@ -41,147 +43,147 @@ #endif #if defined(OSD_WINDOWS) || defined(OSD_SDL) + key_trans_entry keyboard_trans_table::s_default_table[] = { - // MAME key sdl scancode di scancode virtual key ascii ui - KEY_TRANS_ENTRY0(ESC, ESCAPE, ESCAPE, VK_ESCAPE, 27, "ESCAPE"), - KEY_TRANS_ENTRY1(1, 1, 1, '1', '1'), - KEY_TRANS_ENTRY1(2, 2, 2, '2', '2'), - KEY_TRANS_ENTRY1(3, 3, 3, '3', '3'), - KEY_TRANS_ENTRY1(4, 4, 4, '4', '4'), - KEY_TRANS_ENTRY1(5, 5, 5, '5', '5'), - KEY_TRANS_ENTRY1(6, 6, 6, '6', '6'), - KEY_TRANS_ENTRY1(7, 7, 7, '7', '7'), - KEY_TRANS_ENTRY1(8, 8, 8, '8', '8'), - KEY_TRANS_ENTRY1(9, 9, 9, '9', '9'), - KEY_TRANS_ENTRY1(0, 0, 0, '0', '0'), - KEY_TRANS_ENTRY1(MINUS, MINUS, MINUS, VK_OEM_MINUS, '-'), - KEY_TRANS_ENTRY1(EQUALS, EQUALS, EQUALS, VK_OEM_PLUS, '='), - KEY_TRANS_ENTRY1(BACKSPACE, BACKSPACE, BACK, VK_BACK, 8), - KEY_TRANS_ENTRY1(TAB, TAB, TAB, VK_TAB, 9), - KEY_TRANS_ENTRY1(Q, Q, Q, 'Q', 'Q'), - KEY_TRANS_ENTRY1(W, W, W, 'W', 'W'), - KEY_TRANS_ENTRY1(E, E, E, 'E', 'E'), - KEY_TRANS_ENTRY1(R, R, R, 'R', 'R'), - KEY_TRANS_ENTRY1(T, T, T, 'T', 'T'), - KEY_TRANS_ENTRY1(Y, Y, Y, 'Y', 'Y'), - KEY_TRANS_ENTRY1(U, U, U, 'U', 'U'), - KEY_TRANS_ENTRY1(I, I, I, 'I', 'I'), - KEY_TRANS_ENTRY1(O, O, O, 'O', 'O'), - KEY_TRANS_ENTRY1(P, P, P, 'P', 'P'), - KEY_TRANS_ENTRY1(OPENBRACE, LEFTBRACKET, LBRACKET, VK_OEM_4, '['), - KEY_TRANS_ENTRY1(CLOSEBRACE, RIGHTBRACKET, RBRACKET, VK_OEM_6, ']'), - KEY_TRANS_ENTRY0(ENTER, RETURN, RETURN, VK_RETURN, 13, "RETURN"), - KEY_TRANS_ENTRY1(LCONTROL, LCTRL, LCONTROL, VK_LCONTROL, 0), - KEY_TRANS_ENTRY1(A, A, A, 'A', 'A'), - KEY_TRANS_ENTRY1(S, S, S, 'S', 'S'), - KEY_TRANS_ENTRY1(D, D, D, 'D', 'D'), - KEY_TRANS_ENTRY1(F, F, F, 'F', 'F'), - KEY_TRANS_ENTRY1(G, G, G, 'G', 'G'), - KEY_TRANS_ENTRY1(H, H, H, 'H', 'H'), - KEY_TRANS_ENTRY1(J, J, J, 'J', 'J'), - KEY_TRANS_ENTRY1(K, K, K, 'K', 'K'), - KEY_TRANS_ENTRY1(L, L, L, 'L', 'L'), - KEY_TRANS_ENTRY1(COLON, SEMICOLON, SEMICOLON, VK_OEM_1, ';'), - KEY_TRANS_ENTRY1(QUOTE, APOSTROPHE, APOSTROPHE, VK_OEM_7, '\''), - KEY_TRANS_ENTRY1(TILDE, GRAVE, GRAVE, VK_OEM_3, '`'), - KEY_TRANS_ENTRY1(LSHIFT, LSHIFT, LSHIFT, VK_LSHIFT, 0), - KEY_TRANS_ENTRY1(BACKSLASH, BACKSLASH, BACKSLASH, VK_OEM_5, '\\'), + // MAME key SDL scancode DI scancode virtual key ASCII UI + KEY_TRANS_ENTRY0(ESC, ESCAPE, ESCAPE, VK_ESCAPE, 27, "ESCAPE"), + KEY_TRANS_ENTRY1(1, 1, 1, '1', '1'), + KEY_TRANS_ENTRY1(2, 2, 2, '2', '2'), + KEY_TRANS_ENTRY1(3, 3, 3, '3', '3'), + KEY_TRANS_ENTRY1(4, 4, 4, '4', '4'), + KEY_TRANS_ENTRY1(5, 5, 5, '5', '5'), + KEY_TRANS_ENTRY1(6, 6, 6, '6', '6'), + KEY_TRANS_ENTRY1(7, 7, 7, '7', '7'), + KEY_TRANS_ENTRY1(8, 8, 8, '8', '8'), + KEY_TRANS_ENTRY1(9, 9, 9, '9', '9'), + KEY_TRANS_ENTRY1(0, 0, 0, '0', '0'), + KEY_TRANS_ENTRY1(MINUS, MINUS, MINUS, VK_OEM_MINUS, '-'), + KEY_TRANS_ENTRY1(EQUALS, EQUALS, EQUALS, VK_OEM_PLUS, '='), + KEY_TRANS_ENTRY1(BACKSPACE, BACKSPACE, BACK, VK_BACK, 8), + KEY_TRANS_ENTRY1(TAB, TAB, TAB, VK_TAB, 9), + KEY_TRANS_ENTRY1(Q, Q, Q, 'Q', 'Q'), + KEY_TRANS_ENTRY1(W, W, W, 'W', 'W'), + KEY_TRANS_ENTRY1(E, E, E, 'E', 'E'), + KEY_TRANS_ENTRY1(R, R, R, 'R', 'R'), + KEY_TRANS_ENTRY1(T, T, T, 'T', 'T'), + KEY_TRANS_ENTRY1(Y, Y, Y, 'Y', 'Y'), + KEY_TRANS_ENTRY1(U, U, U, 'U', 'U'), + KEY_TRANS_ENTRY1(I, I, I, 'I', 'I'), + KEY_TRANS_ENTRY1(O, O, O, 'O', 'O'), + KEY_TRANS_ENTRY1(P, P, P, 'P', 'P'), + KEY_TRANS_ENTRY1(OPENBRACE, LEFTBRACKET, LBRACKET, VK_OEM_4, '['), + KEY_TRANS_ENTRY1(CLOSEBRACE, RIGHTBRACKET, RBRACKET, VK_OEM_6, ']'), + KEY_TRANS_ENTRY0(ENTER, RETURN, RETURN, VK_RETURN, 13, "RETURN"), + KEY_TRANS_ENTRY1(LCONTROL, LCTRL, LCONTROL, VK_LCONTROL, 0), + KEY_TRANS_ENTRY1(A, A, A, 'A', 'A'), + KEY_TRANS_ENTRY1(S, S, S, 'S', 'S'), + KEY_TRANS_ENTRY1(D, D, D, 'D', 'D'), + KEY_TRANS_ENTRY1(F, F, F, 'F', 'F'), + KEY_TRANS_ENTRY1(G, G, G, 'G', 'G'), + KEY_TRANS_ENTRY1(H, H, H, 'H', 'H'), + KEY_TRANS_ENTRY1(J, J, J, 'J', 'J'), + KEY_TRANS_ENTRY1(K, K, K, 'K', 'K'), + KEY_TRANS_ENTRY1(L, L, L, 'L', 'L'), + KEY_TRANS_ENTRY1(COLON, SEMICOLON, SEMICOLON, VK_OEM_1, ';'), + KEY_TRANS_ENTRY1(QUOTE, APOSTROPHE, APOSTROPHE, VK_OEM_7, '\''), + KEY_TRANS_ENTRY1(TILDE, GRAVE, GRAVE, VK_OEM_3, '`'), + KEY_TRANS_ENTRY1(LSHIFT, LSHIFT, LSHIFT, VK_LSHIFT, 0), + KEY_TRANS_ENTRY1(BACKSLASH, BACKSLASH, BACKSLASH, VK_OEM_5, '\\'), // KEY_TRANS_ENTRY1(BACKSLASH2, NONUSHASH, UNKNOWN, OEM_102, VK_OEM_102, '<'), // This is the additional key that ISO keyboards have over ANSI ones, located between left shift and Y. - KEY_TRANS_ENTRY1(BACKSLASH2, NONUSBACKSLASH, OEM_102, VK_OEM_102, '<'), - KEY_TRANS_ENTRY1(Z, Z, Z, 'Z', 'Z'), - KEY_TRANS_ENTRY1(X, X, X, 'X', 'X'), - KEY_TRANS_ENTRY1(C, C, C, 'C', 'C'), - KEY_TRANS_ENTRY1(V, V, V, 'V', 'V'), - KEY_TRANS_ENTRY1(B, B, B, 'B', 'B'), - KEY_TRANS_ENTRY1(N, N, N, 'N', 'N'), - KEY_TRANS_ENTRY1(M, M, M, 'M', 'M'), - KEY_TRANS_ENTRY1(COMMA, COMMA, COMMA, VK_OEM_COMMA, ','), - KEY_TRANS_ENTRY1(STOP, PERIOD, PERIOD, VK_OEM_PERIOD, '.'), - KEY_TRANS_ENTRY1(SLASH, SLASH, SLASH, VK_OEM_2, '/'), - KEY_TRANS_ENTRY1(RSHIFT, RSHIFT, RSHIFT, VK_RSHIFT, 0), - KEY_TRANS_ENTRY1(ASTERISK, KP_MULTIPLY, MULTIPLY, VK_MULTIPLY, '*'), - KEY_TRANS_ENTRY1(LALT, LALT, LMENU, VK_LMENU, 0), - KEY_TRANS_ENTRY1(SPACE, SPACE, SPACE, VK_SPACE, ' '), - KEY_TRANS_ENTRY1(CAPSLOCK, CAPSLOCK, CAPITAL, VK_CAPITAL, 0), - KEY_TRANS_ENTRY1(F1, F1, F1, VK_F1, 0), - KEY_TRANS_ENTRY1(F2, F2, F2, VK_F2, 0), - KEY_TRANS_ENTRY1(F3, F3, F3, VK_F3, 0), - KEY_TRANS_ENTRY1(F4, F4, F4, VK_F4, 0), - KEY_TRANS_ENTRY1(F5, F5, F5, VK_F5, 0), - KEY_TRANS_ENTRY1(F6, F6, F6, VK_F6, 0), - KEY_TRANS_ENTRY1(F7, F7, F7, VK_F7, 0), - KEY_TRANS_ENTRY1(F8, F8, F8, VK_F8, 0), - KEY_TRANS_ENTRY1(F9, F9, F9, VK_F9, 0), - KEY_TRANS_ENTRY1(F10, F10, F10, VK_F10, 0), - KEY_TRANS_ENTRY1(NUMLOCK, NUMLOCKCLEAR, NUMLOCK, VK_NUMLOCK, 0), - KEY_TRANS_ENTRY1(SCRLOCK, SCROLLLOCK, SCROLL, VK_SCROLL, 0), - KEY_TRANS_ENTRY1(7_PAD, KP_7, NUMPAD7, VK_NUMPAD7, 0), - KEY_TRANS_ENTRY1(8_PAD, KP_8, NUMPAD8, VK_NUMPAD8, 0), - KEY_TRANS_ENTRY1(9_PAD, KP_9, NUMPAD9, VK_NUMPAD9, 0), - KEY_TRANS_ENTRY1(MINUS_PAD, KP_MINUS, SUBTRACT, VK_SUBTRACT, 0), - KEY_TRANS_ENTRY1(4_PAD, KP_4, NUMPAD4, VK_NUMPAD4, 0), - KEY_TRANS_ENTRY1(5_PAD, KP_5, NUMPAD5, VK_NUMPAD5, 0), - KEY_TRANS_ENTRY1(6_PAD, KP_6, NUMPAD6, VK_NUMPAD6, 0), - KEY_TRANS_ENTRY1(PLUS_PAD, KP_PLUS, ADD, VK_ADD, 0), - KEY_TRANS_ENTRY1(1_PAD, KP_1, NUMPAD1, VK_NUMPAD1, 0), - KEY_TRANS_ENTRY1(2_PAD, KP_2, NUMPAD2, VK_NUMPAD2, 0), - KEY_TRANS_ENTRY1(3_PAD, KP_3, NUMPAD3, VK_NUMPAD3, 0), - KEY_TRANS_ENTRY1(0_PAD, KP_0, NUMPAD0, VK_NUMPAD0, 0), - KEY_TRANS_ENTRY1(DEL_PAD, KP_PERIOD, DECIMAL, VK_DECIMAL, 0), - KEY_TRANS_ENTRY1(F11, F11, F11, VK_F11, 0), - KEY_TRANS_ENTRY1(F12, F12, F12, VK_F12, 0), - KEY_TRANS_ENTRY1(F13, F13, F13, VK_F13, 0), - KEY_TRANS_ENTRY1(F14, F14, F14, VK_F14, 0), - KEY_TRANS_ENTRY1(F15, F15, F15, VK_F15, 0), - KEY_TRANS_ENTRY1(ENTER_PAD, KP_ENTER, NUMPADENTER, VK_RETURN, 0), - KEY_TRANS_ENTRY1(RCONTROL, RCTRL, RCONTROL, VK_RCONTROL, 0), - KEY_TRANS_ENTRY1(SLASH_PAD, KP_DIVIDE, DIVIDE, VK_DIVIDE, 0), - KEY_TRANS_ENTRY1(PRTSCR, PRINTSCREEN, SYSRQ, 0, 0), - KEY_TRANS_ENTRY1(RALT, RALT, RMENU, VK_RMENU, 0), - KEY_TRANS_ENTRY1(HOME, HOME, HOME, VK_HOME, 0), - KEY_TRANS_ENTRY1(UP, UP, UP, VK_UP, 0), - KEY_TRANS_ENTRY1(PGUP, PAGEUP, PRIOR, VK_PRIOR, 0), - KEY_TRANS_ENTRY1(LEFT, LEFT, LEFT, VK_LEFT, 0), - KEY_TRANS_ENTRY1(RIGHT, RIGHT, RIGHT, VK_RIGHT, 0), - KEY_TRANS_ENTRY1(END, END, END, VK_END, 0), - KEY_TRANS_ENTRY1(DOWN, DOWN, DOWN, VK_DOWN, 0), - KEY_TRANS_ENTRY1(PGDN, PAGEDOWN, NEXT, VK_NEXT, 0), - KEY_TRANS_ENTRY1(INSERT, INSERT, INSERT, VK_INSERT, 0), - KEY_TRANS_ENTRY0(DEL, DELETE, DELETE, VK_DELETE, 0, "DELETE"), - KEY_TRANS_ENTRY1(LWIN, LGUI, LWIN, VK_LWIN, 0), - KEY_TRANS_ENTRY1(RWIN, RGUI, RWIN, VK_RWIN, 0), - KEY_TRANS_ENTRY1(MENU, MENU, APPS, VK_APPS, 0), - KEY_TRANS_ENTRY1(PAUSE, PAUSE, PAUSE, VK_PAUSE, 0), - KEY_TRANS_ENTRY0(CANCEL, CANCEL, UNKNOWN, 0, 0, "CANCEL"), - KEY_TRANS_ENTRY1(BS_PAD, KP_BACKSPACE, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(TAB_PAD, KP_TAB, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(00_PAD, KP_00, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(000_PAD, KP_000, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(COMMA_PAD, KP_COMMA, NUMPADCOMMA, 0, 0), - KEY_TRANS_ENTRY1(EQUALS_PAD, KP_EQUALS, NUMPADEQUALS, 0, 0), - - // New keys introduced in Windows 2000. These have no MAME codes to - // preserve compatibility with old config files that may refer to them - // as e.g. FORWARD instead of e.g. KEYCODE_WEBFORWARD. They need table - // entries anyway because otherwise they aren't recognized when - // GetAsyncKeyState polling is used (as happens currently when MAME is - // paused). Some codes are missing because the mapping to vkey codes - // isn't clear, and MapVirtualKey is no help. - KEY_TRANS_ENTRY1(OTHER_SWITCH, MUTE, MUTE, VK_VOLUME_MUTE, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, VOLUMEDOWN, VOLUMEDOWN, VK_VOLUME_DOWN, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, VOLUMEUP, VOLUMEUP, VK_VOLUME_UP, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_HOME, WEBHOME, VK_BROWSER_HOME, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_SEARCH, WEBSEARCH, VK_BROWSER_SEARCH, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_BOOKMARKS, WEBFAVORITES, VK_BROWSER_FAVORITES, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_REFRESH, WEBREFRESH, VK_BROWSER_REFRESH, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_STOP, WEBSTOP, VK_BROWSER_STOP, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_FORWARD, WEBFORWARD, VK_BROWSER_FORWARD, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_BACK, WEBBACK, VK_BROWSER_BACK, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, MAIL, MAIL, VK_LAUNCH_MAIL, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, MEDIASELECT, MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, 0), - KEY_TRANS_ENTRY0(INVALID, UNKNOWN, ESCAPE, 0, 0, "INVALID") + KEY_TRANS_ENTRY1(BACKSLASH2, NONUSBACKSLASH, OEM_102, VK_OEM_102, '<'), + KEY_TRANS_ENTRY1(Z, Z, Z, 'Z', 'Z'), + KEY_TRANS_ENTRY1(X, X, X, 'X', 'X'), + KEY_TRANS_ENTRY1(C, C, C, 'C', 'C'), + KEY_TRANS_ENTRY1(V, V, V, 'V', 'V'), + KEY_TRANS_ENTRY1(B, B, B, 'B', 'B'), + KEY_TRANS_ENTRY1(N, N, N, 'N', 'N'), + KEY_TRANS_ENTRY1(M, M, M, 'M', 'M'), + KEY_TRANS_ENTRY1(COMMA, COMMA, COMMA, VK_OEM_COMMA, ','), + KEY_TRANS_ENTRY1(STOP, PERIOD, PERIOD, VK_OEM_PERIOD, '.'), + KEY_TRANS_ENTRY1(SLASH, SLASH, SLASH, VK_OEM_2, '/'), + KEY_TRANS_ENTRY1(RSHIFT, RSHIFT, RSHIFT, VK_RSHIFT, 0), + KEY_TRANS_ENTRY1(ASTERISK, KP_MULTIPLY, MULTIPLY, VK_MULTIPLY, '*'), + KEY_TRANS_ENTRY1(LALT, LALT, LMENU, VK_LMENU, 0), + KEY_TRANS_ENTRY1(SPACE, SPACE, SPACE, VK_SPACE, ' '), + KEY_TRANS_ENTRY1(CAPSLOCK, CAPSLOCK, CAPITAL, VK_CAPITAL, 0), + KEY_TRANS_ENTRY1(F1, F1, F1, VK_F1, 0), + KEY_TRANS_ENTRY1(F2, F2, F2, VK_F2, 0), + KEY_TRANS_ENTRY1(F3, F3, F3, VK_F3, 0), + KEY_TRANS_ENTRY1(F4, F4, F4, VK_F4, 0), + KEY_TRANS_ENTRY1(F5, F5, F5, VK_F5, 0), + KEY_TRANS_ENTRY1(F6, F6, F6, VK_F6, 0), + KEY_TRANS_ENTRY1(F7, F7, F7, VK_F7, 0), + KEY_TRANS_ENTRY1(F8, F8, F8, VK_F8, 0), + KEY_TRANS_ENTRY1(F9, F9, F9, VK_F9, 0), + KEY_TRANS_ENTRY1(F10, F10, F10, VK_F10, 0), + KEY_TRANS_ENTRY1(NUMLOCK, NUMLOCKCLEAR, NUMLOCK, VK_NUMLOCK, 0), + KEY_TRANS_ENTRY1(SCRLOCK, SCROLLLOCK, SCROLL, VK_SCROLL, 0), + KEY_TRANS_ENTRY1(7_PAD, KP_7, NUMPAD7, VK_NUMPAD7, 0), + KEY_TRANS_ENTRY1(8_PAD, KP_8, NUMPAD8, VK_NUMPAD8, 0), + KEY_TRANS_ENTRY1(9_PAD, KP_9, NUMPAD9, VK_NUMPAD9, 0), + KEY_TRANS_ENTRY1(MINUS_PAD, KP_MINUS, SUBTRACT, VK_SUBTRACT, 0), + KEY_TRANS_ENTRY1(4_PAD, KP_4, NUMPAD4, VK_NUMPAD4, 0), + KEY_TRANS_ENTRY1(5_PAD, KP_5, NUMPAD5, VK_NUMPAD5, 0), + KEY_TRANS_ENTRY1(6_PAD, KP_6, NUMPAD6, VK_NUMPAD6, 0), + KEY_TRANS_ENTRY1(PLUS_PAD, KP_PLUS, ADD, VK_ADD, 0), + KEY_TRANS_ENTRY1(1_PAD, KP_1, NUMPAD1, VK_NUMPAD1, 0), + KEY_TRANS_ENTRY1(2_PAD, KP_2, NUMPAD2, VK_NUMPAD2, 0), + KEY_TRANS_ENTRY1(3_PAD, KP_3, NUMPAD3, VK_NUMPAD3, 0), + KEY_TRANS_ENTRY1(0_PAD, KP_0, NUMPAD0, VK_NUMPAD0, 0), + KEY_TRANS_ENTRY1(DEL_PAD, KP_PERIOD, DECIMAL, VK_DECIMAL, 0), + KEY_TRANS_ENTRY1(F11, F11, F11, VK_F11, 0), + KEY_TRANS_ENTRY1(F12, F12, F12, VK_F12, 0), + KEY_TRANS_ENTRY1(F13, F13, F13, VK_F13, 0), + KEY_TRANS_ENTRY1(F14, F14, F14, VK_F14, 0), + KEY_TRANS_ENTRY1(F15, F15, F15, VK_F15, 0), + KEY_TRANS_ENTRY1(ENTER_PAD, KP_ENTER, NUMPADENTER, VK_RETURN, 0), + KEY_TRANS_ENTRY1(RCONTROL, RCTRL, RCONTROL, VK_RCONTROL, 0), + KEY_TRANS_ENTRY1(SLASH_PAD, KP_DIVIDE, DIVIDE, VK_DIVIDE, 0), + KEY_TRANS_ENTRY1(PRTSCR, PRINTSCREEN, SYSRQ, 0, 0), + KEY_TRANS_ENTRY1(RALT, RALT, RMENU, VK_RMENU, 0), + KEY_TRANS_ENTRY1(HOME, HOME, HOME, VK_HOME, 0), + KEY_TRANS_ENTRY1(UP, UP, UP, VK_UP, 0), + KEY_TRANS_ENTRY1(PGUP, PAGEUP, PRIOR, VK_PRIOR, 0), + KEY_TRANS_ENTRY1(LEFT, LEFT, LEFT, VK_LEFT, 0), + KEY_TRANS_ENTRY1(RIGHT, RIGHT, RIGHT, VK_RIGHT, 0), + KEY_TRANS_ENTRY1(END, END, END, VK_END, 0), + KEY_TRANS_ENTRY1(DOWN, DOWN, DOWN, VK_DOWN, 0), + KEY_TRANS_ENTRY1(PGDN, PAGEDOWN, NEXT, VK_NEXT, 0), + KEY_TRANS_ENTRY1(INSERT, INSERT, INSERT, VK_INSERT, 0), + KEY_TRANS_ENTRY0(DEL, DELETE, DELETE, VK_DELETE, 0, "DELETE"), + KEY_TRANS_ENTRY1(LWIN, LGUI, LWIN, VK_LWIN, 0), + KEY_TRANS_ENTRY1(RWIN, RGUI, RWIN, VK_RWIN, 0), + KEY_TRANS_ENTRY1(MENU, MENU, APPS, VK_APPS, 0), + KEY_TRANS_ENTRY1(PAUSE, PAUSE, PAUSE, VK_PAUSE, 0), + KEY_TRANS_ENTRY1(CANCEL, CANCEL, UNKNOWN, 0, 0), + KEY_TRANS_ENTRY1(BS_PAD, KP_BACKSPACE, UNKNOWN, 0, 0), + KEY_TRANS_ENTRY1(TAB_PAD, KP_TAB, UNKNOWN, 0, 0), + KEY_TRANS_ENTRY1(00_PAD, KP_00, UNKNOWN, 0, 0), + KEY_TRANS_ENTRY1(000_PAD, KP_000, UNKNOWN, 0, 0), + KEY_TRANS_ENTRY1(COMMA_PAD, KP_COMMA, NUMPADCOMMA, VK_SEPARATOR, 0), + KEY_TRANS_ENTRY1(EQUALS_PAD, KP_EQUALS, NUMPADEQUALS, VK_OEM_NEC_EQUAL, 0), + + // keys that have no specific MAME input item IDs + KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIONEXT, NEXTTRACK, VK_MEDIA_NEXT_TRACK, 0, "AUDIONEXT"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIOMUTE, MUTE, VK_VOLUME_MUTE, 0, "VOLUMEMUTE"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIOPLAY, PLAYPAUSE, VK_MEDIA_PLAY_PAUSE, 0, "AUDIOPLAY"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AUDIOSTOP, MEDIASTOP, VK_MEDIA_STOP, 0, "AUDIOSTOP"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, VOLUMEDOWN, VOLUMEDOWN, VK_VOLUME_DOWN, 0, "VOLUMEDOWN"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, VOLUMEUP, VOLUMEUP, VK_VOLUME_UP, 0, "VOLUMEUP"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_HOME, WEBHOME, VK_BROWSER_HOME, 0, "NAVHOME"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_SEARCH, WEBSEARCH, VK_BROWSER_SEARCH, 0, "NAVSEARCH"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_BOOKMARKS, WEBFAVORITES, VK_BROWSER_FAVORITES, 0, "NAVFAVORITES"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_REFRESH, WEBREFRESH, VK_BROWSER_REFRESH, 0, "NAVREFRESH"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_STOP, WEBSTOP, VK_BROWSER_STOP, 0, "NAVSTOP"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_FORWARD, WEBFORWARD, VK_BROWSER_FORWARD, 0, "NAVFORWARD"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, AC_BACK, WEBBACK, VK_BROWSER_BACK, 0, "NAVBACK"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, MAIL, MAIL, VK_LAUNCH_MAIL, 0, "MAIL"), + KEY_TRANS_ENTRY0(OTHER_SWITCH, MEDIASELECT, MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, 0, "MEDIASEL"), + + // sentinel + KEY_TRANS_ENTRY0(INVALID, UNKNOWN, UNKNOWN, 0, 0, "INVALID") }; // The private constructor to create the default instance @@ -190,14 +192,18 @@ keyboard_trans_table::keyboard_trans_table() m_table = s_default_table; m_table_size = std::size(s_default_table); } -#else + +#else // defined(OSD_WINDOWS) || defined(OSD_SDL) + keyboard_trans_table::keyboard_trans_table() { m_table = nullptr; m_table_size = 0; } -#endif +#endif // defined(OSD_WINDOWS) || defined(OSD_SDL) + + // public constructor to allow creation of non-default instances keyboard_trans_table::keyboard_trans_table(std::unique_ptr<key_trans_entry[]> entries, unsigned int size) { @@ -248,11 +254,10 @@ int keyboard_trans_table::vkey_for_mame_code(input_code code) const // only works for keyboard switches if (code.device_class() == DEVICE_CLASS_KEYBOARD && code.item_class() == ITEM_CLASS_SWITCH) { - input_item_id id = code.item_id(); - int tablenum; + const input_item_id id = code.item_id(); // scan the table for a match - for (tablenum = 0; tablenum < m_table_size; tablenum++) + for (int tablenum = 0; tablenum < m_table_size; tablenum++) if (m_table[tablenum].mame_key == id) return m_table[tablenum].virtual_key; } diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h index e767d2b07f2..73ddd2bf936 100644 --- a/src/osd/modules/input/input_common.h +++ b/src/osd/modules/input/input_common.h @@ -47,155 +47,6 @@ enum #define MAX_HATS 8 #define MAX_POV 4 -/**************************************************************************** -* DirectInput compatible keyboard scan codes -****************************************************************************/ -#define KEY_UNKNOWN 0x00 -#define KEY_ESCAPE 0x01 -#define KEY_1 0x02 -#define KEY_2 0x03 -#define KEY_3 0x04 -#define KEY_4 0x05 -#define KEY_5 0x06 -#define KEY_6 0x07 -#define KEY_7 0x08 -#define KEY_8 0x09 -#define KEY_9 0x0A -#define KEY_0 0x0B -#define KEY_MINUS 0x0C /* - on main keyboard */ -#define KEY_EQUALS 0x0D -#define KEY_BACK 0x0E /* backspace */ -#define KEY_TAB 0x0F -#define KEY_Q 0x10 -#define KEY_W 0x11 -#define KEY_E 0x12 -#define KEY_R 0x13 -#define KEY_T 0x14 -#define KEY_Y 0x15 -#define KEY_U 0x16 -#define KEY_I 0x17 -#define KEY_O 0x18 -#define KEY_P 0x19 -#define KEY_LBRACKET 0x1A -#define KEY_RBRACKET 0x1B -#define KEY_RETURN 0x1C /* Enter on main keyboard */ -#define KEY_LCONTROL 0x1D -#define KEY_A 0x1E -#define KEY_S 0x1F -#define KEY_D 0x20 -#define KEY_F 0x21 -#define KEY_G 0x22 -#define KEY_H 0x23 -#define KEY_J 0x24 -#define KEY_K 0x25 -#define KEY_L 0x26 -#define KEY_SEMICOLON 0x27 -#define KEY_APOSTROPHE 0x28 -#define KEY_GRAVE 0x29 /* accent grave */ -#define KEY_LSHIFT 0x2A -#define KEY_BACKSLASH 0x2B -#define KEY_Z 0x2C -#define KEY_X 0x2D -#define KEY_C 0x2E -#define KEY_V 0x2F -#define KEY_B 0x30 -#define KEY_N 0x31 -#define KEY_M 0x32 -#define KEY_COMMA 0x33 -#define KEY_PERIOD 0x34 /* . on main keyboard */ -#define KEY_SLASH 0x35 /* / on main keyboard */ -#define KEY_RSHIFT 0x36 -#define KEY_MULTIPLY 0x37 /* * on numeric keypad */ -#define KEY_LMENU 0x38 /* left Alt */ -#define KEY_SPACE 0x39 -#define KEY_CAPITAL 0x3A -#define KEY_F1 0x3B -#define KEY_F2 0x3C -#define KEY_F3 0x3D -#define KEY_F4 0x3E -#define KEY_F5 0x3F -#define KEY_F6 0x40 -#define KEY_F7 0x41 -#define KEY_F8 0x42 -#define KEY_F9 0x43 -#define KEY_F10 0x44 -#define KEY_NUMLOCK 0x45 -#define KEY_SCROLL 0x46 /* Scroll Lock */ -#define KEY_NUMPAD7 0x47 -#define KEY_NUMPAD8 0x48 -#define KEY_NUMPAD9 0x49 -#define KEY_SUBTRACT 0x4A /* - on numeric keypad */ -#define KEY_NUMPAD4 0x4B -#define KEY_NUMPAD5 0x4C -#define KEY_NUMPAD6 0x4D -#define KEY_ADD 0x4E /* + on numeric keypad */ -#define KEY_NUMPAD1 0x4F -#define KEY_NUMPAD2 0x50 -#define KEY_NUMPAD3 0x51 -#define KEY_NUMPAD0 0x52 -#define KEY_DECIMAL 0x53 /* . on numeric keypad */ -#define KEY_OEM_102 0x56 /* <> or \| on RT 102-key keyboard (Non-U.S.) */ -#define KEY_F11 0x57 -#define KEY_F12 0x58 -#define KEY_F13 0x64 /* (NEC PC98) */ -#define KEY_F14 0x65 /* (NEC PC98) */ -#define KEY_F15 0x66 /* (NEC PC98) */ -#define KEY_KANA 0x70 /* (Japanese keyboard) */ -#define KEY_ABNT_C1 0x73 /* /? on Brazilian keyboard */ -#define KEY_CONVERT 0x79 /* (Japanese keyboard) */ -#define KEY_NOCONVERT 0x7B /* (Japanese keyboard) */ -#define KEY_YEN 0x7D /* (Japanese keyboard) */ -#define KEY_ABNT_C2 0x7E /* Numpad . on Brazilian keyboard */ -#define KEY_NUMPADEQUALS 0x8D /* = on numeric keypad (NEC PC98) */ -#define KEY_PREVTRACK 0x90 /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */ -#define KEY_AT 0x91 /* (NEC PC98) */ -#define KEY_COLON 0x92 /* (NEC PC98) */ -#define KEY_UNDERLINE 0x93 /* (NEC PC98) */ -#define KEY_KANJI 0x94 /* (Japanese keyboard) */ -#define KEY_STOP 0x95 /* (NEC PC98) */ -#define KEY_AX 0x96 /* (Japan AX) */ -#define KEY_UNLABELED 0x97 /* (J3100) */ -#define KEY_NEXTTRACK 0x99 /* Next Track */ -#define KEY_NUMPADENTER 0x9C /* Enter on numeric keypad */ -#define KEY_RCONTROL 0x9D -#define KEY_MUTE 0xA0 /* Mute */ -#define KEY_CALCULATOR 0xA1 /* Calculator */ -#define KEY_PLAYPAUSE 0xA2 /* Play / Pause */ -#define KEY_MEDIASTOP 0xA4 /* Media Stop */ -#define KEY_VOLUMEDOWN 0xAE /* Volume - */ -#define KEY_VOLUMEUP 0xB0 /* Volume + */ -#define KEY_WEBHOME 0xB2 /* Web home */ -#define KEY_NUMPADCOMMA 0xB3 /* , on numeric keypad (NEC PC98) */ -#define KEY_DIVIDE 0xB5 /* / on numeric keypad */ -#define KEY_SYSRQ 0xB7 -#define KEY_RMENU 0xB8 /* right Alt */ -#define KEY_PAUSE 0xC5 /* Pause */ -#define KEY_HOME 0xC7 /* Home on arrow keypad */ -#define KEY_UP 0xC8 /* UpArrow on arrow keypad */ -#define KEY_PRIOR 0xC9 /* PgUp on arrow keypad */ -#define KEY_LEFT 0xCB /* LeftArrow on arrow keypad */ -#define KEY_RIGHT 0xCD /* RightArrow on arrow keypad */ -#define KEY_END 0xCF /* End on arrow keypad */ -#define KEY_DOWN 0xD0 /* DownArrow on arrow keypad */ -#define KEY_NEXT 0xD1 /* PgDn on arrow keypad */ -#define KEY_INSERT 0xD2 /* Insert on arrow keypad */ -#define KEY_DELETE 0xD3 /* Delete on arrow keypad */ -#define KEY_LWIN 0xDB /* Left Windows key */ -#define KEY_RWIN 0xDC /* Right Windows key */ -#define KEY_APPS 0xDD /* AppMenu key */ -#define KEY_POWER 0xDE /* System Power */ -#define KEY_SLEEP 0xDF /* System Sleep */ -#define KEY_WAKE 0xE3 /* System Wake */ -#define KEY_WEBSEARCH 0xE5 /* Web Search */ -#define KEY_WEBFAVORITES 0xE6 /* Web Favorites */ -#define KEY_WEBREFRESH 0xE7 /* Web Refresh */ -#define KEY_WEBSTOP 0xE8 /* Web Stop */ -#define KEY_WEBFORWARD 0xE9 /* Web Forward */ -#define KEY_WEBBACK 0xEA /* Web Back */ -#define KEY_MYCOMPUTER 0xEB /* My Computer */ -#define KEY_MAIL 0xEC /* Mail */ -#define KEY_MEDIASELECT 0xED /* Media Select */ - //============================================================ // device_info @@ -240,7 +91,7 @@ template <class TEvent> class event_based_device : public device_info { private: - static inline constexpr unsigned DEFAULT_EVENT_QUEUE_SIZE = 20; + static inline constexpr unsigned DEFAULT_EVENT_QUEUE_SIZE = 64; std::queue<TEvent> m_event_queue; @@ -266,7 +117,7 @@ public: m_event_queue.pop(); } - void virtual poll(bool relative_reset) override + virtual void poll(bool relative_reset) override { std::lock_guard<std::mutex> scope_lock(m_device_lock); @@ -277,6 +128,12 @@ public: m_event_queue.pop(); } } + + virtual void reset() override + { + std::lock_guard<std::mutex> scope_lock(m_device_lock); + std::queue<TEvent>().swap(m_event_queue); + } }; @@ -342,7 +199,7 @@ struct key_trans_entry int sdl_scancode; #endif #if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) - int scan_code; + uint16_t scan_code; unsigned char virtual_key; #endif diff --git a/src/osd/modules/input/input_dinput.cpp b/src/osd/modules/input/input_dinput.cpp index eec5904b3ac..6f539c2f7fc 100644 --- a/src/osd/modules/input/input_dinput.cpp +++ b/src/osd/modules/input/input_dinput.cpp @@ -194,7 +194,7 @@ void dinput_keyboard_device::configure(input_device &device) { // populate it char defname[20]; - for (int keynum = 0; keynum < MAX_KEYS; keynum++) + for (unsigned keynum = 0; keynum < MAX_KEYS; keynum++) { input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum); @@ -232,7 +232,7 @@ public: virtual void configure(input_device &device) override; private: - mouse_state m_mouse; + DIMOUSESTATE2 m_mouse; }; dinput_mouse_device::dinput_mouse_device( diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp index 3ee8e2d8f7e..5b6813dcd59 100644 --- a/src/osd/modules/input/input_rawinput.cpp +++ b/src/osd/modules/input/input_rawinput.cpp @@ -25,9 +25,11 @@ #include <algorithm> #include <cassert> +#include <chrono> #include <functional> #include <mutex> #include <new> +#include <utility> // standard windows headers #include <windows.h> @@ -307,23 +309,75 @@ class rawinput_keyboard_device : public rawinput_device public: rawinput_keyboard_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) : rawinput_device(std::move(name), std::move(id), module, handle), + m_pause_pressed(std::chrono::steady_clock::time_point::min()), + m_e1(0xffff), m_keyboard({ { 0 } }) { } virtual void reset() override { + rawinput_device::reset(); + m_pause_pressed = std::chrono::steady_clock::time_point::min(); memset(&m_keyboard, 0, sizeof(m_keyboard)); + m_e1 = 0xffff; + } + + virtual void poll(bool relative_reset) override + { + rawinput_device::poll(relative_reset); + if (m_keyboard.state[0x80 | 0x45] && (std::chrono::steady_clock::now() > (m_pause_pressed + std::chrono::milliseconds(30)))) + m_keyboard.state[0x80 | 0x45] = 0x00; } virtual void process_event(RAWINPUT const &rawinput) override { // determine the full DIK-compatible scancode - uint8_t scancode = (rawinput.data.keyboard.MakeCode & 0x7f) | ((rawinput.data.keyboard.Flags & RI_KEY_E0) ? 0x80 : 0x00); + uint8_t scancode; - // scancode 0xaa is a special shift code we need to ignore - if (scancode == 0xaa) + // the only thing that uses this is Pause + if (rawinput.data.keyboard.Flags & RI_KEY_E1) + { + m_e1 = rawinput.data.keyboard.MakeCode; return; + } + else if (0xffff != m_e1) + { + auto const e1 = std::exchange(m_e1, 0xffff); + if (!(rawinput.data.keyboard.Flags & RI_KEY_E0)) + { + if (((e1 & ~USHORT(0x80)) == 0x1d) && ((rawinput.data.keyboard.MakeCode & ~USHORT(0x80)) == 0x45)) + { + if (rawinput.data.keyboard.Flags & RI_KEY_BREAK) + return; // RawInput generates a fake break immediately after the make - ignore it + + m_pause_pressed = std::chrono::steady_clock::now(); + scancode = 0x80 | 0x45; + } + else + { + return; // no idea + } + } + else + { + return; // shouldn't happen, ignore it + } + } + else + { + // strip bit 7 of the make code to work around dodgy drivers that set it for key up events + if (rawinput.data.keyboard.MakeCode & ~USHORT(0xff)) + { + // won't fit in a byte along with the E0 flag + return; + } + scancode = (rawinput.data.keyboard.MakeCode & 0x7f) | ((rawinput.data.keyboard.Flags & RI_KEY_E0) ? 0x80 : 0x00); + + // fake shift generated with cursor control and Ins/Del for compatibility with very old DOS software + if (scancode == 0xaa) + return; + } // set or clear the key m_keyboard.state[scancode] = (rawinput.data.keyboard.Flags & RI_KEY_BREAK) ? 0x00 : 0x80; @@ -333,13 +387,20 @@ public: { keyboard_trans_table const &table = keyboard_trans_table::instance(); - for (int keynum = 0; keynum < MAX_KEYS; keynum++) + // FIXME: GetKeyNameTextW is for scan codes from WM_KEYDOWN, which aren't quite the same as DIK_* keycodes + // in particular, NumLock and Pause are reversed for US-style keyboard systems + for (unsigned keynum = 0; keynum < MAX_KEYS; keynum++) { input_item_id itemid = table.map_di_scancode_to_itemid(keynum); WCHAR keyname[100]; // generate the name - if (GetKeyNameTextW(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, std::size(keyname)) == 0) + // FIXME: GetKeyNameText gives bogus names for media keys and various other things + // in many cases it ignores the "extended" bit and returns the key name corresponding to the scan code alone + LONG lparam = ((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17); + if ((keynum & 0x7f) == 0x45) + lparam ^= 0x0100'0000; // horrid hack + if (GetKeyNameTextW(lparam, keyname, std::size(keyname)) == 0) _snwprintf(keyname, std::size(keyname), L"Scan%03d", keynum); std::string name = text::from_wstring(keyname); @@ -354,6 +415,8 @@ public: } private: + std::chrono::steady_clock::time_point m_pause_pressed; + uint16_t m_e1; keyboard_state m_keyboard; }; @@ -370,7 +433,8 @@ public: m_mouse({0}), m_x(0), m_y(0), - m_z(0) + m_v(0), + m_h(0) { } @@ -381,28 +445,45 @@ public: { m_mouse.lX = std::exchange(m_x, 0); m_mouse.lY = std::exchange(m_y, 0); - m_mouse.lZ = std::exchange(m_z, 0); + m_mouse.lV = std::exchange(m_v, 0); + m_mouse.lH = std::exchange(m_h, 0); } } virtual void reset() override { + rawinput_device::reset(); memset(&m_mouse, 0, sizeof(m_mouse)); - m_x = m_y = m_z = 0; + m_x = m_y = m_v = m_h = 0; } virtual void configure(input_device &device) override { // populate the axes - for (int axisnum = 0; axisnum < 3; axisnum++) - { - device.add_item( - default_axis_name[axisnum], - std::string_view(), - input_item_id(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &m_mouse.lX + axisnum); - } + device.add_item( + "X", + std::string_view(), + ITEM_ID_XAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lX); + device.add_item( + "Y", + std::string_view(), + ITEM_ID_YAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lY); + device.add_item( + "Scroll V", + std::string_view(), + ITEM_ID_ZAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lV); + device.add_item( + "Scroll H", + std::string_view(), + ITEM_ID_RZAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lH); // populate the buttons for (int butnum = 0; butnum < 5; butnum++) @@ -418,17 +499,17 @@ public: virtual void process_event(RAWINPUT const &rawinput) override { - // If this data was intended for a rawinput mouse if (rawinput.data.mouse.usFlags == MOUSE_MOVE_RELATIVE) { - m_x += rawinput.data.mouse.lLastX * input_device::RELATIVE_PER_PIXEL; m_y += rawinput.data.mouse.lLastY * input_device::RELATIVE_PER_PIXEL; - // update Z axis (vertical scroll) + // update Z/Rz axes (vertical/horizontal scroll) if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) - m_z += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL; + m_v += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_HWHEEL) + m_h += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL; // update the button states; always update the corresponding mouse buttons if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_mouse.rgbButtons[0] = 0x80; @@ -446,7 +527,7 @@ public: private: mouse_state m_mouse; - LONG m_x, m_y, m_z; + LONG m_x, m_y, m_v, m_h; }; @@ -460,7 +541,8 @@ public: rawinput_lightgun_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) : rawinput_device(std::move(name), std::move(id), module, handle), m_lightgun({0}), - m_z(0) + m_v(0), + m_h(0) { } @@ -468,13 +550,18 @@ public: { rawinput_device::poll(relative_reset); if (relative_reset) - m_lightgun.lZ = std::exchange(m_z, 0); + { + m_lightgun.lV = std::exchange(m_v, 0); + m_lightgun.lH = std::exchange(m_h, 0); + } } virtual void reset() override { + rawinput_device::reset(); memset(&m_lightgun, 0, sizeof(m_lightgun)); - m_z = 0; + m_v = 0; + m_h = 0; } virtual void configure(input_device &device) override @@ -490,13 +577,19 @@ public: &m_lightgun.lX + axisnum); } - // scroll wheel is always relative if present + // scroll wheels are always relative if present device.add_item( - default_axis_name[2], + "Scroll V", std::string_view(), ITEM_ID_ADD_RELATIVE1, generic_axis_get_state<LONG>, - &m_lightgun.lZ); + &m_lightgun.lV); + device.add_item( + "Scroll H", + std::string_view(), + ITEM_ID_ADD_RELATIVE2, + generic_axis_get_state<LONG>, + &m_lightgun.lH); // populate the buttons for (int butnum = 0; butnum < 5; butnum++) @@ -515,14 +608,15 @@ public: // If this data was intended for a rawinput lightgun if (rawinput.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { - // update the X/Y positions m_lightgun.lX = normalize_absolute_axis(rawinput.data.mouse.lLastX, 0, input_device::ABSOLUTE_MAX); m_lightgun.lY = normalize_absolute_axis(rawinput.data.mouse.lLastY, 0, input_device::ABSOLUTE_MAX); - // update zaxis + // update Z/Rz axes if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) - m_z += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL; + m_v += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_HWHEEL) + m_h += int16_t(rawinput.data.mouse.usButtonData) * input_device::RELATIVE_PER_PIXEL; // update the button states; always update the corresponding mouse buttons if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_lightgun.rgbButtons[0] = 0x80; @@ -540,12 +634,12 @@ public: private: mouse_state m_lightgun; - LONG m_z; + LONG m_v, m_h; }; //============================================================ -// rawinput_module - base class for rawinput modules +// rawinput_module - base class for RawInput modules //============================================================ class rawinput_module : public wininput_module<rawinput_device> @@ -608,9 +702,7 @@ public: RAWINPUTDEVICE registration; registration.usUsagePage = usagepage(); registration.usUsage = usage(); - registration.dwFlags = RIDEV_DEVNOTIFY; - if (background_input()) - registration.dwFlags |= RIDEV_INPUTSINK; + registration.dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; registration.hwndTarget = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); // register the device @@ -654,18 +746,18 @@ protected: rawinputdevice.hDevice); } - virtual bool handle_input_event(input_event eventid, void *eventdata) override + virtual bool handle_input_event(input_event eventid, void const *eventdata) override { switch (eventid) { // handle raw input data case INPUT_EVENT_RAWINPUT: { - HRAWINPUT const rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); + HRAWINPUT const rawinputdevice = *reinterpret_cast<HRAWINPUT const *>(eventdata); - BYTE small_buffer[4096]; + union { RAWINPUT r; BYTE b[4096]; } small_buffer; std::unique_ptr<BYTE []> larger_buffer; - LPBYTE data = small_buffer; + LPVOID data = &small_buffer; UINT size; // determine the size of data buffer we need @@ -675,7 +767,7 @@ protected: // if necessary, allocate a temporary buffer and fetch the data if (size > sizeof(small_buffer)) { - larger_buffer.reset(new (std::nothrow) BYTE [size]); + larger_buffer.reset(new (std::align_val_t(alignof(RAWINPUT)), std::nothrow) BYTE [size]); data = larger_buffer.get(); if (!data) return false; @@ -691,7 +783,7 @@ protected: { std::lock_guard<std::mutex> scope_lock(m_module_lock); - auto *input = reinterpret_cast<RAWINPUT *>(data); + auto *const input = reinterpret_cast<RAWINPUT const *>(data); if (!input->header.hDevice) return false; @@ -714,7 +806,7 @@ protected: case INPUT_EVENT_ARRIVAL: { - HRAWINPUT const rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); + HRAWINPUT const rawinputdevice = *reinterpret_cast<HRAWINPUT const *>(eventdata); // determine the length of the device name, allocate it, and fetch it if not nameless UINT name_length = 0; @@ -747,7 +839,7 @@ protected: case INPUT_EVENT_REMOVAL: { - HRAWINPUT const rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); + HRAWINPUT const rawinputdevice = *reinterpret_cast<HRAWINPUT const *>(eventdata); std::lock_guard<std::mutex> scope_lock(m_module_lock); @@ -780,7 +872,7 @@ protected: //============================================================ -// keyboard_input_rawinput - rawinput keyboard module +// keyboard_input_rawinput - RawInput keyboard module //============================================================ class keyboard_input_rawinput : public rawinput_module @@ -807,7 +899,7 @@ protected: //============================================================ -// mouse_input_rawinput - rawinput mouse module +// mouse_input_rawinput - RawInput mouse module //============================================================ class mouse_input_rawinput : public rawinput_module @@ -834,7 +926,7 @@ protected: //============================================================ -// lightgun_input_rawinput - rawinput lightgun module +// lightgun_input_rawinput - RawInput lightgun module //============================================================ class lightgun_input_rawinput : public rawinput_module diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp index 1eaed99f16b..f1fb0241b40 100644 --- a/src/osd/modules/input/input_sdl.cpp +++ b/src/osd/modules/input/input_sdl.cpp @@ -33,6 +33,7 @@ #include <algorithm> #include <cctype> #include <chrono> +#include <cmath> #include <cstddef> #include <cstring> #include <initializer_list> @@ -63,7 +64,7 @@ char const *const CONTROLLER_AXIS_XBOX[]{ "LT", "RT" }; -[[maybe_unused]] char const *const CONTROLLER_AXIS_PS[]{ +char const *const CONTROLLER_AXIS_PS[]{ "LSX", "LSY", "RSX", @@ -71,7 +72,7 @@ char const *const CONTROLLER_AXIS_XBOX[]{ "L2", "R2" }; -[[maybe_unused]] char const *const CONTROLLER_AXIS_SWITCH[]{ +char const *const CONTROLLER_AXIS_SWITCH[]{ "LSX", "LSY", "RSX", @@ -102,7 +103,7 @@ char const *const CONTROLLER_BUTTON_XBOX360[]{ "P4", "Touchpad" }; -[[maybe_unused]] char const *const CONTROLLER_BUTTON_XBOXONE[]{ +char const *const CONTROLLER_BUTTON_XBOXONE[]{ "A", "B", "X", @@ -125,7 +126,7 @@ char const *const CONTROLLER_BUTTON_XBOX360[]{ "P4", "Touchpad" }; -[[maybe_unused]] char const *const CONTROLLER_BUTTON_PS3[]{ +char const *const CONTROLLER_BUTTON_PS3[]{ "Cross", "Circle", "Square", @@ -148,7 +149,7 @@ char const *const CONTROLLER_BUTTON_XBOX360[]{ "P4", "Touchpad" }; -[[maybe_unused]] char const *const CONTROLLER_BUTTON_PS4[]{ +char const *const CONTROLLER_BUTTON_PS4[]{ "Cross", "Circle", "Square", @@ -171,7 +172,7 @@ char const *const CONTROLLER_BUTTON_XBOX360[]{ "P4", "Touchpad" }; -[[maybe_unused]] char const *const CONTROLLER_BUTTON_PS5[]{ +char const *const CONTROLLER_BUTTON_PS5[]{ "Cross", "Circle", "Square", @@ -194,7 +195,7 @@ char const *const CONTROLLER_BUTTON_XBOX360[]{ "P4", "Touchpad" }; -[[maybe_unused]] char const *const CONTROLLER_BUTTON_SWITCH[]{ +char const *const CONTROLLER_BUTTON_SWITCH[]{ "A", "B", "X", @@ -607,6 +608,7 @@ public: virtual void reset() override { + sdl_device::reset(); memset(&m_keyboard.state, 0, sizeof(m_keyboard.state)); m_capslock_pressed = std::chrono::steady_clock::time_point::min(); } @@ -617,13 +619,8 @@ public: for (int keynum = 0; m_trans_table[keynum].mame_key != ITEM_ID_INVALID; keynum++) { input_item_id itemid = m_trans_table[keynum].mame_key; - - // generate the default / modified name - char defname[20]; - snprintf(defname, sizeof(defname) - 1, "%s", m_trans_table[keynum].ui_name); - device.add_item( - defname, + m_trans_table[keynum].ui_name, std::string_view(), itemid, generic_button_get_state<s32>, @@ -647,29 +644,18 @@ private: //============================================================ -// sdl_mouse_device +// sdl_mouse_device_base //============================================================ -class sdl_mouse_device : public sdl_device +class sdl_mouse_device_base : public sdl_device { public: - sdl_mouse_device(std::string &&name, std::string &&id, input_module &module) : - sdl_device(std::move(name), std::move(id), module), - m_mouse({0}), - m_x(0), - m_y(0), - m_v(0), - m_h(0) - { - } - virtual void poll(bool relative_reset) override { sdl_device::poll(relative_reset); + if (relative_reset) { - m_mouse.lX = std::exchange(m_x, 0); - m_mouse.lY = std::exchange(m_y, 0); m_mouse.lV = std::exchange(m_v, 0); m_mouse.lH = std::exchange(m_h, 0); } @@ -677,13 +663,30 @@ public: virtual void reset() override { + sdl_device::reset(); memset(&m_mouse, 0, sizeof(m_mouse)); - m_x = m_y = m_v = m_h = 0; + m_v = m_h = 0; } - virtual void configure(input_device &device) override +protected: + // state information for a mouse + struct mouse_state { - // add the axes + s32 lX, lY, lV, lH; + s32 buttons[MAX_BUTTONS]; + }; + + sdl_mouse_device_base(std::string &&name, std::string &&id, input_module &module) : + sdl_device(std::move(name), std::move(id), module), + m_mouse({0}), + m_v(0), + m_h(0) + { + } + + void add_common_items(input_device &device, unsigned buttons) + { + // add horizontal and vertical axes - relative for a mouse or absolute for a gun device.add_item( "X", std::string_view(), @@ -696,6 +699,62 @@ public: ITEM_ID_YAXIS, generic_axis_get_state<s32>, &m_mouse.lY); + + // add buttons + for (int button = 0; button < buttons; button++) + { + input_item_id itemid = (input_item_id)(ITEM_ID_BUTTON1 + button); + int const offset = button ^ (((1 == button) || (2 == button)) ? 3 : 0); + device.add_item( + default_button_name(button), + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_mouse.buttons[offset]); + } + } + + mouse_state m_mouse; + s32 m_v, m_h; +}; + + +//============================================================ +// sdl_mouse_device +//============================================================ + +class sdl_mouse_device : public sdl_mouse_device_base +{ +public: + sdl_mouse_device(std::string &&name, std::string &&id, input_module &module) : + sdl_mouse_device_base(std::move(name), std::move(id), module), + m_x(0), + m_y(0) + { + } + + virtual void poll(bool relative_reset) override + { + sdl_mouse_device_base::poll(relative_reset); + + if (relative_reset) + { + m_mouse.lX = std::exchange(m_x, 0); + m_mouse.lY = std::exchange(m_y, 0); + } + } + + virtual void reset() override + { + sdl_mouse_device_base::reset(); + m_x = m_y = 0; + } + + virtual void configure(input_device &device) override + { + add_common_items(device, 5); + + // add scroll axes device.add_item( "Scroll V", std::string_view(), @@ -708,60 +767,206 @@ public: ITEM_ID_RZAXIS, generic_axis_get_state<s32>, &m_mouse.lH); + } + + virtual void process_event(SDL_Event const &event) override + { + switch (event.type) + { + case SDL_MOUSEMOTION: + m_x += event.motion.xrel * input_device::RELATIVE_PER_PIXEL; + m_y += event.motion.yrel * input_device::RELATIVE_PER_PIXEL; + break; + + case SDL_MOUSEBUTTONDOWN: + m_mouse.buttons[event.button.button - 1] = 0x80; + break; + + case SDL_MOUSEBUTTONUP: + m_mouse.buttons[event.button.button - 1] = 0; + break; + + case SDL_MOUSEWHEEL: + // adjust SDL 1-per-click to match Win32 120-per-click +#if SDL_VERSION_ATLEAST(2, 0, 18) + m_v += std::lround(event.wheel.preciseY * 120 * input_device::RELATIVE_PER_PIXEL); + m_h += std::lround(event.wheel.preciseX * 120 * input_device::RELATIVE_PER_PIXEL); +#else + m_v += event.wheel.y * 120 * input_device::RELATIVE_PER_PIXEL; + m_h += event.wheel.x * 120 * input_device::RELATIVE_PER_PIXEL; +#endif + break; + } + } + +private: + s32 m_x, m_y; +}; + + +//============================================================ +// sdl_lightgun_device +//============================================================ + +class sdl_lightgun_device : public sdl_mouse_device_base +{ +public: + sdl_lightgun_device(std::string &&name, std::string &&id, input_module &module) : + sdl_mouse_device_base(std::move(name), std::move(id), module), + m_x(0), + m_y(0), + m_window(0) + { + } + + virtual void poll(bool relative_reset) override + { + sdl_mouse_device_base::poll(relative_reset); - // add the buttons - for (int button = 0; button < 4; button++) + SDL_Window *const win(m_window ? SDL_GetWindowFromID(m_window) : nullptr); + if (win) { - input_item_id itemid = (input_item_id)(ITEM_ID_BUTTON1 + button); - int const offset = button ^ (((1 == button) || (2 == button)) ? 3 : 0); - device.add_item( - default_button_name(button), - std::string_view(), - itemid, - generic_button_get_state<s32>, - &m_mouse.buttons[offset]); + int w, h; + SDL_GetWindowSize(win, &w, &h); + m_mouse.lX = normalize_absolute_axis(m_x, 0, w - 1); + m_mouse.lY = normalize_absolute_axis(m_y, 0, h - 1); + } + else + { + m_mouse.lX = 0; + m_mouse.lY = 0; } } + virtual void reset() override + { + sdl_mouse_device_base::reset(); + m_x = m_y = 0; + m_window = 0; + } + + virtual void configure(input_device &device) override + { + add_common_items(device, 5); + + // add scroll axes + device.add_item( + "Scroll V", + std::string_view(), + ITEM_ID_ADD_RELATIVE1, + generic_axis_get_state<s32>, + &m_mouse.lV); + device.add_item( + "Scroll H", + std::string_view(), + ITEM_ID_ADD_RELATIVE2, + generic_axis_get_state<s32>, + &m_mouse.lH); + } + virtual void process_event(SDL_Event const &event) override { switch (event.type) { case SDL_MOUSEMOTION: - m_x += event.motion.xrel * input_device::RELATIVE_PER_PIXEL; - m_y += event.motion.yrel * input_device::RELATIVE_PER_PIXEL; + m_x = event.motion.x; + m_y = event.motion.y; + m_window = event.motion.windowID; break; case SDL_MOUSEBUTTONDOWN: m_mouse.buttons[event.button.button - 1] = 0x80; + m_x = event.button.x; + m_y = event.button.y; + m_window = event.button.windowID; break; case SDL_MOUSEBUTTONUP: m_mouse.buttons[event.button.button - 1] = 0; + m_x = event.button.x; + m_y = event.button.y; + m_window = event.button.windowID; break; case SDL_MOUSEWHEEL: + // adjust SDL 1-per-click to match Win32 120-per-click #if SDL_VERSION_ATLEAST(2, 0, 18) - m_v += event.wheel.preciseY * input_device::RELATIVE_PER_PIXEL; - m_h += event.wheel.preciseX * input_device::RELATIVE_PER_PIXEL; + m_v += std::lround(event.wheel.preciseY * 120 * input_device::RELATIVE_PER_PIXEL); + m_h += std::lround(event.wheel.preciseX * 120 * input_device::RELATIVE_PER_PIXEL); #else - m_v += event.wheel.y * input_device::RELATIVE_PER_PIXEL; - m_h += event.wheel.x * input_device::RELATIVE_PER_PIXEL; + m_v += event.wheel.y * 120 * input_device::RELATIVE_PER_PIXEL; + m_h += event.wheel.x * 120 * input_device::RELATIVE_PER_PIXEL; #endif break; + + case SDL_WINDOWEVENT: + if ((event.window.windowID == m_window) && (SDL_WINDOWEVENT_LEAVE == event.window.event)) + m_window = 0; + break; } } private: - // state information for a mouse - struct mouse_state + s32 m_x, m_y; + u32 m_window; +}; + + +//============================================================ +// sdl_dual_lightgun_device +//============================================================ + +class sdl_dual_lightgun_device : public sdl_mouse_device_base +{ +public: + sdl_dual_lightgun_device(std::string &&name, std::string &&id, input_module &module, u8 index) : + sdl_mouse_device_base(std::move(name), std::move(id), module), + m_index(index) { - s32 lX, lY, lV, lH; - s32 buttons[MAX_BUTTONS]; - }; + } - mouse_state m_mouse; - s32 m_x, m_y, m_v, m_h; + virtual void configure(input_device &device) override + { + add_common_items(device, 2); + } + + virtual void process_event(SDL_Event const &event) override + { + switch (event.type) + { + case SDL_MOUSEBUTTONDOWN: + { + SDL_Window *const win(SDL_GetWindowFromID(event.button.windowID)); + u8 const button = translate_button(event); + if (win && ((button / 2) == m_index)) + { + int w, h; + SDL_GetWindowSize(win, &w, &h); + m_mouse.buttons[(button & 1) << 1] = 0x80; + m_mouse.lX = normalize_absolute_axis(event.button.x, 0, w - 1); + m_mouse.lY = normalize_absolute_axis(event.button.y, 0, h - 1); + } + } + break; + + case SDL_MOUSEBUTTONUP: + { + u8 const button = translate_button(event); + if ((button / 2) == m_index) + m_mouse.buttons[(button & 1) << 1] = 0; + } + break; + } + } + +private: + static u8 translate_button(SDL_Event const &event) + { + u8 const index(event.button.button - 1); + return index ^ (((1 == index) || (2 == index)) ? 3 : 0); + } + + u8 const m_index; }; @@ -1113,25 +1318,20 @@ public: ~sdl_joystick_device() { - if (m_joydevice) - { - if (m_hapdevice) - { - SDL_HapticClose(m_hapdevice); - m_hapdevice = nullptr; - } - SDL_JoystickClose(m_joydevice); - m_joydevice = nullptr; - } + close_device(); } virtual void reset() override { - memset(&m_joystick, 0, sizeof(m_joystick)); + sdl_joystick_device_base::reset(); + clear_buffer(); } virtual void process_event(SDL_Event const &event) override { + if (!m_joydevice) + return; + switch (event.type) { case SDL_JOYAXISMOTION: @@ -1167,17 +1367,8 @@ public: case SDL_JOYDEVICEREMOVED: osd_printf_verbose("Joystick: %s [ID %s] disconnected\n", name(), id()); clear_instance(); - reset(); - if (m_joydevice) - { - if (m_hapdevice) - { - SDL_HapticClose(m_hapdevice); - m_hapdevice = nullptr; - } - SDL_JoystickClose(m_joydevice); - m_joydevice = nullptr; - } + clear_buffer(); + close_device(); break; } } @@ -1214,6 +1405,25 @@ protected: private: SDL_Joystick *m_joydevice; SDL_Haptic *m_hapdevice; + + void clear_buffer() + { + memset(&m_joystick, 0, sizeof(m_joystick)); + } + + void close_device() + { + if (m_joydevice) + { + if (m_hapdevice) + { + SDL_HapticClose(m_hapdevice); + m_hapdevice = nullptr; + } + SDL_JoystickClose(m_joydevice); + m_joydevice = nullptr; + } + } }; @@ -1280,11 +1490,7 @@ public: ~sdl_game_controller_device() { - if (m_ctrldevice) - { - SDL_GameControllerClose(m_ctrldevice); - m_ctrldevice = nullptr; - } + close_device(); } virtual void configure(input_device &device) override @@ -1293,10 +1499,7 @@ public: char const *const *axisnames = CONTROLLER_AXIS_XBOX; char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360; bool digitaltriggers = false; -#if SDL_VERSION_ATLEAST(2, 0, 14) bool avoidpaddles = false; -#endif -#if SDL_VERSION_ATLEAST(2, 0, 12) auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice); switch (ctrltype) { @@ -1329,14 +1532,12 @@ public: buttonnames = CONTROLLER_BUTTON_SWITCH; digitaltriggers = true; break; -#if SDL_VERSION_ATLEAST(2, 0, 14) //case SDL_CONTROLLER_TYPE_VIRTUAL: case SDL_CONTROLLER_TYPE_PS5: osd_printf_verbose("Game Controller: ... PlayStation 5 type\n"); axisnames = CONTROLLER_AXIS_PS; buttonnames = CONTROLLER_BUTTON_PS5; break; -#endif #if SDL_VERSION_ATLEAST(2, 0, 16) //case SDL_CONTROLLER_TYPE_AMAZON_LUNA: case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: @@ -1365,7 +1566,6 @@ public: osd_printf_verbose("Game Controller: ... unrecognized type (%d)\n", int(ctrltype)); break; } -#endif // keep track of item numbers as we add controls std::pair<input_item_id, input_item_id> axisitems[SDL_CONTROLLER_AXIS_MAX]; @@ -1395,9 +1595,7 @@ public: for (auto [axis, item, buttontest] : axes) { bool avail = !buttontest || !digitaltriggers; -#if SDL_VERSION_ATLEAST(2, 0, 14) avail = avail && SDL_GameControllerHasAxis(m_ctrldevice, axis); -#endif if (avail) { auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); @@ -1437,17 +1635,13 @@ public: { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, true }, { SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_AXIS_INVALID, true }, { SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_AXIS_INVALID, true }, -#if SDL_VERSION_ATLEAST(2, 0, 14) { SDL_CONTROLLER_BUTTON_PADDLE1, SDL_CONTROLLER_AXIS_INVALID, true }, { SDL_CONTROLLER_BUTTON_PADDLE2, SDL_CONTROLLER_AXIS_INVALID, true }, { SDL_CONTROLLER_BUTTON_PADDLE3, SDL_CONTROLLER_AXIS_INVALID, true }, { SDL_CONTROLLER_BUTTON_PADDLE4, SDL_CONTROLLER_AXIS_INVALID, true }, -#endif { SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_AXIS_INVALID, false }, -#if SDL_VERSION_ATLEAST(2, 0, 14) { SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_AXIS_INVALID, false }, { SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_AXIS_INVALID, false }, -#endif }; input_item_id button_item = ITEM_ID_BUTTON1; unsigned buttoncount = 0; @@ -1457,9 +1651,7 @@ public: input_item_id actual = ITEM_ID_INVALID; if (SDL_CONTROLLER_BUTTON_INVALID != button) { -#if SDL_VERSION_ATLEAST(2, 0, 14) avail = SDL_GameControllerHasButton(m_ctrldevice, button); -#endif if (avail) { auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button); @@ -1486,9 +1678,7 @@ public: } else { -#if SDL_VERSION_ATLEAST(2, 0, 14) avail = SDL_GameControllerHasAxis(m_ctrldevice, axis); -#endif if (avail) { auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); @@ -1538,9 +1728,7 @@ public: for (auto [button, item] : fixedbuttons) { bool avail = true; -#if SDL_VERSION_ATLEAST(2, 0, 14) avail = SDL_GameControllerHasButton(m_ctrldevice, button); -#endif if (avail) { auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button); @@ -1611,7 +1799,6 @@ public: { // took trigger buttons } -#if SDL_VERSION_ATLEAST(2, 0, 14) else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2])) { // took P1/P2 @@ -1620,7 +1807,6 @@ public: { // took P3/P4 } -#endif } // prefer trigger axes for pedals, otherwise take half axes and buttons @@ -1706,22 +1892,18 @@ public: { // took digital triggers } -#if SDL_VERSION_ATLEAST(2, 0, 14) else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2])) { // took upper paddles } -#endif else if (consume_trigger_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)) { // took analog triggers } -#if SDL_VERSION_ATLEAST(2, 0, 14) else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4])) { // took lower paddles } -#endif else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1])) { // took secondary Y @@ -1732,7 +1914,6 @@ public: } // try to get a matching pair of buttons for page up/down -#if SDL_VERSION_ATLEAST(2, 0, 14) if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE1], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE2])) { // took upper paddles @@ -1742,7 +1923,6 @@ public: // took lower paddles } else -#endif if (consume_trigger_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)) { // took analog triggers @@ -1871,11 +2051,15 @@ public: virtual void reset() override { - memset(&m_controller, 0, sizeof(m_controller)); + sdl_joystick_device_base::reset(); + clear_buffer(); } virtual void process_event(SDL_Event const &event) override { + if (!m_ctrldevice) + return; + switch (event.type) { case SDL_CONTROLLERAXISMOTION: @@ -1902,12 +2086,8 @@ public: case SDL_CONTROLLERDEVICEREMOVED: osd_printf_verbose("Game Controller: %s [ID %s] disconnected\n", name(), id()); clear_instance(); - reset(); - if (m_ctrldevice) - { - SDL_GameControllerClose(m_ctrldevice); - m_ctrldevice = nullptr; - } + clear_buffer(); + close_device(); break; } } @@ -1933,6 +2113,20 @@ private: sdl_controller_state m_controller; SDL_GameController *m_ctrldevice; + + void clear_buffer() + { + memset(&m_controller, 0, sizeof(m_controller)); + } + + void close_device() + { + if (m_ctrldevice) + { + SDL_GameControllerClose(m_ctrldevice); + m_ctrldevice = nullptr; + } + } }; @@ -1977,8 +2171,7 @@ class sdl_keyboard_module : public sdl_input_module<sdl_keyboard_device> { public: sdl_keyboard_module() : - sdl_input_module<sdl_keyboard_device>(OSD_KEYBOARDINPUT_PROVIDER, "sdl"), - m_key_trans_table(nullptr) + sdl_input_module<sdl_keyboard_device>(OSD_KEYBOARDINPUT_PROVIDER, "sdl") { } @@ -1986,14 +2179,14 @@ public: { sdl_input_module<sdl_keyboard_device>::input_init(machine); - static int const event_types[] = { + constexpr int event_types[] = { int(SDL_KEYDOWN), int(SDL_KEYUP) }; subscribe(osd(), event_types); // Read our keymap and store a pointer to our table - m_key_trans_table = sdlinput_read_keymap(); + sdlinput_read_keymap(); osd_printf_verbose("Keyboard: Start initialization\n"); @@ -2009,12 +2202,27 @@ public: } private: - keyboard_trans_table *sdlinput_read_keymap() + void sdlinput_read_keymap() { keyboard_trans_table &default_table = keyboard_trans_table::instance(); + // Allocate a block of translation entries big enough to hold what's in the default table + auto key_trans_entries = std::make_unique<key_trans_entry []>(default_table.size()); + + // copy the elements from the default table and ask SDL for key names + for (int i = 0; i < default_table.size(); i++) + { + key_trans_entries[i] = default_table[i]; + char const *const name = SDL_GetScancodeName(SDL_Scancode(default_table[i].sdl_scancode)); + if (name && *name) + key_trans_entries[i].ui_name = name; + } + + // Allocate the trans table to be associated with the machine so we don't have to free it + m_key_trans_table = std::make_unique<keyboard_trans_table>(std::move(key_trans_entries), default_table.size()); + if (!options()->bool_value(SDLOPTION_KEYMAP)) - return &default_table; + return; const char *const keymap_filename = dynamic_cast<sdl_options const &>(*options()).keymap_file(); osd_printf_verbose("Keymap: Start reading keymap_file %s\n", keymap_filename); @@ -2023,19 +2231,9 @@ private: if (!keymap_file) { osd_printf_warning("Keymap: Unable to open keymap %s, using default\n", keymap_filename); - return &default_table; + return; } - // Allocate a block of translation entries big enough to hold what's in the default table - auto key_trans_entries = std::make_unique<key_trans_entry[]>(default_table.size()); - - // copy the elements from the default table - for (int i = 0; i < default_table.size(); i++) - key_trans_entries[i] = default_table[i]; - - // Allocate the trans table to be associated with the machine so we don't have to free it - m_custom_table = std::make_unique<keyboard_trans_table>(std::move(key_trans_entries), default_table.size()); - int line = 1; int sdl2section = 0; while (!feof(keymap_file)) @@ -2068,7 +2266,7 @@ private: if (sk >= 0 && index >= 0) { - key_trans_entry &entry = (*m_custom_table)[index]; + key_trans_entry &entry = (*m_key_trans_table)[index]; entry.sdl_scancode = sk; entry.ui_name = const_cast<char *>(m_ui_names.emplace_back(kns).c_str()); osd_printf_verbose("Keymap: Mapped <%s> to <%s> with ui-text <%s>\n", sks, mks, kns); @@ -2083,12 +2281,9 @@ private: } fclose(keymap_file); osd_printf_verbose("Keymap: Processed %d lines\n", line); - - return m_custom_table.get(); } - keyboard_trans_table *m_key_trans_table; - std::unique_ptr<keyboard_trans_table> m_custom_table; + std::unique_ptr<keyboard_trans_table> m_key_trans_table; std::list<std::string> m_ui_names; }; @@ -2108,7 +2303,7 @@ public: { sdl_input_module::input_init(machine); - static int const event_types[] = { + constexpr int event_types[] = { int(SDL_MOUSEMOTION), int(SDL_MOUSEBUTTONDOWN), int(SDL_MOUSEBUTTONUP), @@ -2131,6 +2326,73 @@ public: //============================================================ +// sdl_lightgun_module +//============================================================ + +class sdl_lightgun_module : public sdl_input_module<sdl_mouse_device_base> +{ +public: + sdl_lightgun_module() : sdl_input_module<sdl_mouse_device_base>(OSD_LIGHTGUNINPUT_PROVIDER, "sdl") + { + } + + virtual void input_init(running_machine &machine) override + { + auto &sdlopts = dynamic_cast<sdl_options const &>(*options()); + sdl_input_module::input_init(machine); + bool const dual(sdlopts.dual_lightgun()); + + if (!dual) + { + constexpr int event_types[] = { + int(SDL_MOUSEMOTION), + int(SDL_MOUSEBUTTONDOWN), + int(SDL_MOUSEBUTTONUP), + int(SDL_MOUSEWHEEL), + int(SDL_WINDOWEVENT) }; + subscribe(osd(), event_types); + } + else + { + constexpr int event_types[] = { + int(SDL_MOUSEBUTTONDOWN), + int(SDL_MOUSEBUTTONUP) }; + subscribe(osd(), event_types); + } + + osd_printf_verbose("Lightgun: Start initialization\n"); + + if (!dual) + { + auto &devinfo = create_device<sdl_lightgun_device>( + DEVICE_CLASS_LIGHTGUN, + "System pointer gun 1", + "System pointer gun 1"); + osd_printf_verbose("Lightgun: Registered %s\n", devinfo.name()); + } + else + { + auto &dev1info = create_device<sdl_dual_lightgun_device>( + DEVICE_CLASS_LIGHTGUN, + "System pointer gun 1", + "System pointer gun 1", + 0); + osd_printf_verbose("Lightgun: Registered %s\n", dev1info.name()); + + auto &dev2info = create_device<sdl_dual_lightgun_device>( + DEVICE_CLASS_LIGHTGUN, + "System pointer gun 2", + "System pointer gun 2", + 1); + osd_printf_verbose("Lightgun: Registered %s\n", dev2info.name()); + } + + osd_printf_verbose("Lightgun: End initialization\n"); + } +}; + + +//============================================================ // sdl_joystick_module_base //============================================================ @@ -2201,10 +2463,7 @@ protected: char guid_str[256]; guid_str[0] = '\0'; SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); - char const *serial = nullptr; -#if SDL_VERSION_ATLEAST(2, 0, 14) - serial = SDL_JoystickGetSerial(joy); -#endif + char const *const serial = SDL_JoystickGetSerial(joy); std::string id(guid_str); if (serial) id.append(1, '-').append(serial); @@ -2316,7 +2575,7 @@ public: for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) create_joystick_device(physical_stick, sixaxis_mode); - static int const event_types[] = { + constexpr int event_types[] = { int(SDL_JOYAXISMOTION), int(SDL_JOYBALLMOTION), int(SDL_JOYHATMOTION), @@ -2341,10 +2600,7 @@ public: else { SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); - char const *serial = nullptr; -#if SDL_VERSION_ATLEAST(2, 0, 14) - serial = SDL_JoystickGetSerial(joy); -#endif + char const *const serial = SDL_JoystickGetSerial(joy); auto *const target_device = find_reconnect_match(guid, serial); if (target_device) { @@ -2440,7 +2696,7 @@ public: create_game_controller_device(physical_stick, ctrl); } - static int const joy_event_types[] = { + constexpr int joy_event_types[] = { int(SDL_JOYAXISMOTION), int(SDL_JOYBALLMOTION), int(SDL_JOYHATMOTION), @@ -2448,7 +2704,7 @@ public: int(SDL_JOYBUTTONUP), int(SDL_JOYDEVICEADDED), int(SDL_JOYDEVICEREMOVED) }; - static int const event_types[] = { + constexpr int event_types[] = { int(SDL_JOYAXISMOTION), int(SDL_JOYBALLMOTION), int(SDL_JOYHATMOTION), @@ -2494,10 +2750,7 @@ public: } SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); - char const *serial = nullptr; -#if SDL_VERSION_ATLEAST(2, 0, 14) - serial = SDL_JoystickGetSerial(joy); -#endif + char const *const serial = SDL_JoystickGetSerial(joy); auto *const target_device = find_reconnect_match(guid, serial); if (target_device) { @@ -2527,10 +2780,7 @@ public: } SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(event.cdevice.which); - char const *serial = nullptr; -#if SDL_VERSION_ATLEAST(2, 0, 14) - serial = SDL_GameControllerGetSerial(ctrl); -#endif + char const *const serial = SDL_GameControllerGetSerial(ctrl); auto *const target_device = find_reconnect_match(guid, serial); if (target_device) { @@ -2562,10 +2812,7 @@ private: char guid_str[256]; guid_str[0] = '\0'; SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); - char const *serial = nullptr; -#if SDL_VERSION_ATLEAST(2, 0, 14) - serial = SDL_GameControllerGetSerial(ctrl); -#endif + char const *const serial = SDL_GameControllerGetSerial(ctrl); std::string id(guid_str); if (serial) id.append(1, '-').append(serial); @@ -2615,6 +2862,7 @@ namespace { MODULE_NOT_SUPPORTED(sdl_keyboard_module, OSD_KEYBOARDINPUT_PROVIDER, "sdl") MODULE_NOT_SUPPORTED(sdl_mouse_module, OSD_MOUSEINPUT_PROVIDER, "sdl") +MODULE_NOT_SUPPORTED(sdl_lightgun_module, OSD_LIGHTGUNINPUT_PROVIDER, "sdl") MODULE_NOT_SUPPORTED(sdl_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdljoy") MODULE_NOT_SUPPORTED(sdl_game_controller_module, OSD_JOYSTICKINPUT_PROVIDER, "sdlgame") @@ -2627,5 +2875,6 @@ MODULE_NOT_SUPPORTED(sdl_game_controller_module, OSD_JOYSTICKINPUT_PROVIDER, "sd MODULE_DEFINITION(KEYBOARDINPUT_SDL, osd::sdl_keyboard_module) MODULE_DEFINITION(MOUSEINPUT_SDL, osd::sdl_mouse_module) +MODULE_DEFINITION(LIGHTGUNINPUT_SDL, osd::sdl_lightgun_module) MODULE_DEFINITION(JOYSTICKINPUT_SDLJOY, osd::sdl_joystick_module) MODULE_DEFINITION(JOYSTICKINPUT_SDLGAME, osd::sdl_game_controller_module) diff --git a/src/osd/modules/input/input_win32.cpp b/src/osd/modules/input/input_win32.cpp index 6919c8d5c42..921c902ad31 100644 --- a/src/osd/modules/input/input_win32.cpp +++ b/src/osd/modules/input/input_win32.cpp @@ -46,6 +46,7 @@ public: virtual void reset() override { + event_based_device::reset(); memset(&m_keyboard, 0, sizeof(m_keyboard)); } @@ -59,6 +60,8 @@ public: TCHAR keyname[100]; // generate the name + // FIXME: GetKeyNameText gives bogus names for media keys and various other things + // in many cases it ignores the "extended" bit and returns the key name corresponding to the scan code alone if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, std::size(keyname)) == 0) _sntprintf(keyname, std::size(keyname), TEXT("Scan%03d"), keynum); std::string name = text::from_tstring(keyname); @@ -103,14 +106,14 @@ public: create_device<win32_keyboard_device>(DEVICE_CLASS_KEYBOARD, "Win32 Keyboard 1", "Win32 Keyboard 1"); } - virtual bool handle_input_event(input_event eventid, void *eventdata) override + virtual bool handle_input_event(input_event eventid, void const *eventdata) override { switch (eventid) { case INPUT_EVENT_KEYDOWN: case INPUT_EVENT_KEYUP: devicelist().for_each_device( - [args = static_cast<KeyPressEventArgs const *>(eventdata)] (auto &device) + [args = reinterpret_cast<KeyPressEventArgs const *>(eventdata)] (auto &device) { device.queue_events(args, 1); }); @@ -127,13 +130,15 @@ public: // win32_mouse_device //============================================================ -class win32_mouse_device : public event_based_device<MouseButtonEventArgs> +class win32_mouse_device : public event_based_device<MouseUpdateEventArgs> { public: win32_mouse_device(std::string &&name, std::string &&id, input_module &module) : event_based_device(std::move(name), std::move(id), module), m_mouse({0}), - m_win32_mouse({{0}}) + m_win32_mouse({{0}}), + m_vscroll(0), + m_hscroll(0) { } @@ -144,7 +149,7 @@ public: if (!relative_reset) return; - CURSORINFO cursor_info = {0}; + CURSORINFO cursor_info = { 0 }; cursor_info.cbSize = sizeof(CURSORINFO); GetCursorInfo(&cursor_info); @@ -167,23 +172,42 @@ public: SetCursorPos(m_win32_mouse.last_point.x, m_win32_mouse.last_point.y); } + + // update scroll axes + m_mouse.lV = std::exchange(m_vscroll, 0) * input_device::RELATIVE_PER_PIXEL; + m_mouse.lH = std::exchange(m_hscroll, 0) * input_device::RELATIVE_PER_PIXEL; } virtual void configure(input_device &device) override { // populate the axes - for (int axisnum = 0; axisnum < 2; axisnum++) - { - device.add_item( - default_axis_name[axisnum], - std::string_view(), - input_item_id(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &m_mouse.lX + axisnum); - } + device.add_item( + "X", + std::string_view(), + ITEM_ID_XAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lX); + device.add_item( + "Y", + std::string_view(), + ITEM_ID_YAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lY); + device.add_item( + "Scroll V", + std::string_view(), + ITEM_ID_ZAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lV); + device.add_item( + "Scroll H", + std::string_view(), + ITEM_ID_RZAXIS, + generic_axis_get_state<LONG>, + &m_mouse.lH); // populate the buttons - for (int butnum = 0; butnum < 2; butnum++) + for (int butnum = 0; butnum < 5; butnum++) { device.add_item( default_button_name(butnum), @@ -196,15 +220,28 @@ public: virtual void reset() override { + event_based_device::reset(); memset(&m_mouse, 0, sizeof(m_mouse)); memset(&m_win32_mouse, 0, sizeof(m_win32_mouse)); + m_vscroll = m_hscroll = 0; } protected: - virtual void process_event(MouseButtonEventArgs const &args) override + virtual void process_event(MouseUpdateEventArgs const &args) override { // set the button state - m_mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00; + assert(!(args.pressed & args.released)); + for (unsigned i = 0; 5 > i; ++i) + { + if (BIT(args.pressed, i)) + m_mouse.rgbButtons[i] = 0x80; + else if (BIT(args.released, i)) + m_mouse.rgbButtons[i] = 0x00; + } + + // accumulate scroll delta + m_vscroll += args.vdelta; + m_hscroll += args.hdelta; } private: @@ -215,6 +252,7 @@ private: mouse_state m_mouse; win32_mouse_state m_win32_mouse; + long m_vscroll, m_hscroll; }; @@ -240,16 +278,20 @@ public: create_device<win32_mouse_device>(DEVICE_CLASS_MOUSE, "Win32 Mouse 1", "Win32 Mouse 1"); } - virtual bool handle_input_event(input_event eventid, void *eventdata) override + virtual bool handle_input_event(input_event eventid, void const *eventdata) override { - if (!manager().class_enabled(DEVICE_CLASS_MOUSE) || eventid != INPUT_EVENT_MOUSE_BUTTON) - return false; - - auto const *const args = static_cast<MouseButtonEventArgs *>(eventdata); - devicelist().for_each_device( - [args] (auto &device) { device.queue_events(args, 1); }); + if (manager().class_enabled(DEVICE_CLASS_MOUSE)) + { + if ((eventid == INPUT_EVENT_MOUSE_BUTTON) || (eventid == INPUT_EVENT_MOUSE_WHEEL)) + { + auto const *const args = reinterpret_cast<MouseUpdateEventArgs const *>(eventdata); + devicelist().for_each_device( + [args] (auto &device) { device.queue_events(args, 1); }); + return true; + } + } - return true; + return false; } }; @@ -258,15 +300,26 @@ public: // win32_lightgun_device_base //============================================================ -class win32_lightgun_device_base : public event_based_device<MouseButtonEventArgs> +class win32_lightgun_device_base : public event_based_device<MouseUpdateEventArgs> { public: virtual void reset() override { + event_based_device::reset(); memset(&m_mouse, 0, sizeof(m_mouse)); } - virtual void configure(input_device &device) override +protected: + win32_lightgun_device_base( + std::string &&name, + std::string &&id, + input_module &module) : + event_based_device(std::move(name), std::move(id), module), + m_mouse({ 0 }) + { + } + + void do_configure(input_device &device, unsigned buttons) { // populate the axes for (int axisnum = 0; axisnum < 2; axisnum++) @@ -280,7 +333,7 @@ public: } // populate the buttons - for (int butnum = 0; butnum < 2; butnum++) + for (int butnum = 0; butnum < buttons; butnum++) { device.add_item( default_button_name(butnum), @@ -291,16 +344,6 @@ public: } } -protected: - win32_lightgun_device_base( - std::string &&name, - std::string &&id, - input_module &module) : - event_based_device(std::move(name), std::move(id), module), - m_mouse({ 0 }) - { - } - mouse_state m_mouse; }; @@ -317,7 +360,9 @@ public: std::string &&name, std::string &&id, input_module &module) : - win32_lightgun_device_base(std::move(name), std::move(id), module) + win32_lightgun_device_base(std::move(name), std::move(id), module), + m_vscroll(0), + m_hscroll(0) { } @@ -346,14 +391,60 @@ public: // update the X/Y positions m_mouse.lX = xpos; m_mouse.lY = ypos; + + // update the scroll axes if appropriate + if (relative_reset) + { + m_mouse.lV = std::exchange(m_vscroll, 0) * input_device::RELATIVE_PER_PIXEL; + m_mouse.lH = std::exchange(m_hscroll, 0) * input_device::RELATIVE_PER_PIXEL; + } + } + + virtual void configure(input_device &device) override + { + do_configure(device, 5); + + // add scroll axes + device.add_item( + "Scroll V", + std::string_view(), + ITEM_ID_ADD_RELATIVE1, + generic_axis_get_state<LONG>, + &m_mouse.lV); + device.add_item( + "Scroll H", + std::string_view(), + ITEM_ID_ADD_RELATIVE2, + generic_axis_get_state<LONG>, + &m_mouse.lH); + } + + virtual void reset() override + { + win32_lightgun_device_base::reset(); + m_vscroll = m_hscroll = 0; } protected: - virtual void process_event(MouseButtonEventArgs const &args) override + virtual void process_event(MouseUpdateEventArgs const &args) override { // In non-shared axis mode, just update the button state - m_mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00; + assert(!(args.pressed & args.released)); + for (unsigned i = 0; 5 > i; ++i) + { + if (BIT(args.pressed, i)) + m_mouse.rgbButtons[i] = 0x80; + else if (BIT(args.released, i)) + m_mouse.rgbButtons[i] = 0x00; + } + + // accumulate scroll delta + m_vscroll += args.vdelta; + m_hscroll += args.hdelta; } + +private: + long m_vscroll, m_hscroll; }; @@ -374,38 +465,42 @@ public: { } -protected: - virtual void process_event(MouseButtonEventArgs const &args) override + virtual void configure(input_device &device) override { - int const button = args.button; - - if (button > 3) - return; // We only handle the first four buttons in shared axis mode - else if (button >= 2 && m_gun_index == 0) - return; // First gun doesn't handle buttons 2 and 3 - else if (button < 2 && m_gun_index == 1) - return; // Second gun doesn't handle buttons 0 and 1 - - // Adjust the button if we're the second lightgun - int const logical_button = (m_gun_index == 1) ? (button - 2) : button; + do_configure(device, 2); + } - // set the button state - m_mouse.rgbButtons[logical_button] = args.keydown ? 0x80 : 0x00; - if (args.keydown) +protected: + virtual void process_event(MouseUpdateEventArgs const &args) override + { + // We only handle the first four buttons in shared axis mode + assert(!(args.pressed & args.released)); + for (unsigned i = 0; 2 > i; ++i) { - // get the position relative to the window - HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); - RECT client_rect; - GetClientRect(hwnd, &client_rect); - - POINT mousepos; - mousepos.x = args.xpos; - mousepos.y = args.ypos; - ScreenToClient(hwnd, &mousepos); - - // convert to absolute coordinates - m_mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); - m_mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); + // Adjust the button if we're the second lightgun + unsigned const bit = i + ((1 == m_gun_index) ? 2 : 0); + if (BIT(args.pressed, bit)) + { + m_mouse.rgbButtons[i] = 0x80; + + // get the position relative to the window + HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); + RECT client_rect; + GetClientRect(hwnd, &client_rect); + + POINT mousepos; + mousepos.x = args.xpos; + mousepos.y = args.ypos; + ScreenToClient(hwnd, &mousepos); + + // convert to absolute coordinates + m_mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); + m_mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); + } + else if (BIT(args.released, bit)) + { + m_mouse.rgbButtons[i] = 0x00; + } } } @@ -445,16 +540,20 @@ public: } } - virtual bool handle_input_event(input_event eventid, void *eventdata) override + virtual bool handle_input_event(input_event eventid, void const *eventdata) override { - if (!manager().class_enabled(DEVICE_CLASS_LIGHTGUN) || eventid != INPUT_EVENT_MOUSE_BUTTON) - return false; - - auto const *const args = static_cast<MouseButtonEventArgs *>(eventdata); - devicelist().for_each_device( - [args] (auto &device) { device.queue_events(args, 1); }); + if (manager().class_enabled(DEVICE_CLASS_LIGHTGUN)) + { + if ((eventid == INPUT_EVENT_MOUSE_BUTTON) || (eventid == INPUT_EVENT_MOUSE_WHEEL)) + { + auto const *const args = reinterpret_cast<MouseUpdateEventArgs const *>(eventdata); + devicelist().for_each_device( + [args] (auto &device) { device.queue_events(args, 1); }); + return true; + } + } - return true; + return false; } }; diff --git a/src/osd/modules/input/input_wincommon.h b/src/osd/modules/input/input_wincommon.h index d77ab8dce9a..6b34d60abca 100644 --- a/src/osd/modules/input/input_wincommon.h +++ b/src/osd/modules/input/input_wincommon.h @@ -25,12 +25,13 @@ struct keyboard_state int8_t currkey[MAX_KEYS]; }; -// state information for a mouse (matches DIMOUSESTATE exactly) +// state information for a mouse struct mouse_state { LONG lX; LONG lY; - LONG lZ; + LONG lV; + LONG lH; BYTE rgbButtons[8]; }; diff --git a/src/osd/modules/input/input_windows.cpp b/src/osd/modules/input/input_windows.cpp index 1db0e3ac02c..12002ccf893 100644 --- a/src/osd/modules/input/input_windows.cpp +++ b/src/osd/modules/input/input_windows.cpp @@ -40,7 +40,7 @@ bool windows_osd_interface::should_hide_mouse() const return true; } -bool windows_osd_interface::handle_input_event(input_event eventid, void *eventdata) const +bool windows_osd_interface::handle_input_event(input_event eventid, void const *eventdata) const { bool handled = false; @@ -80,6 +80,7 @@ void windows_osd_interface::customize_input_type_list(std::vector<input_type_ent case IPT_UI_MENU: entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_TAB, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_RALT); break; + // configurable UI mode switch case IPT_UI_TOGGLE_UI: { @@ -131,10 +132,15 @@ void windows_osd_interface::customize_input_type_list(std::vector<input_type_ent entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LALT); break; - // lctrl-lalt-F5 to toggle post-processing + // lalt-F10 to toggle post-processing case IPT_OSD_4: entry.configure_osd("POST_PROCESS", N_p("input-name", "Toggle Post-Processing")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, KEYCODE_LALT, KEYCODE_LCONTROL); + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, KEYCODE_LALT); + break; + + // add a Not LALT condition to the throttle key + case IPT_UI_THROTTLE: + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, input_seq::not_code, KEYCODE_LALT); break; // leave everything else alone diff --git a/src/osd/modules/input/input_windows.h b/src/osd/modules/input/input_windows.h index 175151cb4e5..882f80d053b 100644 --- a/src/osd/modules/input/input_windows.h +++ b/src/osd/modules/input/input_windows.h @@ -31,7 +31,7 @@ protected: virtual ~wininput_event_handler() = default; public: - virtual bool handle_input_event(input_event eventid, void *data) + virtual bool handle_input_event(input_event eventid, void const *data) { return false; } diff --git a/src/osd/modules/lib/osdlib_macosx.cpp b/src/osd/modules/lib/osdlib_macosx.cpp index 9d13f118ff2..54836f9ec69 100644 --- a/src/osd/modules/lib/osdlib_macosx.cpp +++ b/src/osd/modules/lib/osdlib_macosx.cpp @@ -56,6 +56,7 @@ void osd_process_kill() kill(getpid(), SIGKILL); } + //============================================================ // osd_break_into_debugger //============================================================ @@ -82,6 +83,22 @@ void osd_break_into_debugger(const char *message) //============================================================ +// osd_get_cache_line_size +//============================================================ + +std::pair<std::error_condition, unsigned> osd_get_cache_line_size() noexcept +{ + size_t result = 0; + size_t resultsize = sizeof(result); + int const err = sysctlbyname("hw.cachelinesize", &result, &resultsize, 0, 0); + if (!err) + return std::make_pair(std::error_condition(), unsigned(result)); + else + return std::make_pair(std::error_condition(err, std::generic_category()), 0U); +} + + +//============================================================ // osd_get_clipboard_text //============================================================ diff --git a/src/osd/modules/lib/osdlib_unix.cpp b/src/osd/modules/lib/osdlib_unix.cpp index 20f6a9ab40c..1cfa9b1f183 100644 --- a/src/osd/modules/lib/osdlib_unix.cpp +++ b/src/osd/modules/lib/osdlib_unix.cpp @@ -17,8 +17,10 @@ #include <csignal> #include <cstdio> #include <cstdlib> +#include <cstring> #include <iomanip> #include <memory> +#include <string_view> #include <dlfcn.h> #include <sys/mman.h> @@ -53,21 +55,86 @@ void osd_process_kill() kill(getpid(), SIGKILL); } + //============================================================ // osd_break_into_debugger //============================================================ void osd_break_into_debugger(const char *message) { -#ifdef MAME_DEBUG - printf("MAME exception: %s\n", message); - printf("Attempting to fall into debugger\n"); - kill(getpid(), SIGTRAP); +#if defined(__linux__) + bool do_break = false; + FILE *const f = std::fopen("/proc/self/status", "r"); + if (f) + { + using namespace std::literals; + + std::string_view const tag = "TracerPid:\t"sv; + char buf[128]; + bool ignore = false; + while (std::fgets(buf, std::size(buf), f)) + { + // ignore excessively long lines + auto const len = strnlen(buf, std::size(buf)); + bool const noeol = !len || ('\n' != buf[len - 1]); + if (ignore || noeol) + { + ignore = noeol; + continue; + } + + if (!std::strncmp(buf, tag.data(), tag.length())) + { + long tpid; + if ((std::sscanf(buf + tag.length(), "%ld", &tpid) == 1) && (0 != tpid)) + do_break = true; + break; + } + } + std::fclose(f); + } +#elif defined(MAME_DEBUG) + bool const do_break = true; #else - printf("Ignoring MAME exception: %s\n", message); + bool const do_break = false; +#endif + if (do_break) + { + printf("MAME exception: %s\n", message); + printf("Attempting to fall into debugger\n"); + kill(getpid(), SIGTRAP); + } + else + { + printf("Ignoring MAME exception: %s\n", message); + } +} + + +//============================================================ +// osd_get_cache_line_size +//============================================================ + +std::pair<std::error_condition, unsigned> osd_get_cache_line_size() noexcept +{ +#if defined(__linux__) + FILE *const f = std::fopen("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size", "r"); + if (!f) + return std::make_pair(std::error_condition(errno, std::generic_category()), 0U); + + unsigned result = 0; + auto const cnt = std::fscanf(f, "%u", &result); + std::fclose(f); + if (1 == cnt) + return std::make_pair(std::error_condition(), result); + else + return std::make_pair(std::errc::io_error, 0U); +#else // defined(__linux__) + return std::make_pair(std::errc::not_supported, 0U); #endif } + #ifdef SDLMAME_ANDROID std::string osd_get_clipboard_text() noexcept { diff --git a/src/osd/modules/lib/osdlib_win32.cpp b/src/osd/modules/lib/osdlib_win32.cpp index d42ad14d3df..cf3fd7c9320 100644 --- a/src/osd/modules/lib/osdlib_win32.cpp +++ b/src/osd/modules/lib/osdlib_win32.cpp @@ -106,6 +106,42 @@ void osd_break_into_debugger(const char *message) #endif } + +//============================================================ +// osd_get_cache_line_size +//============================================================ + +std::pair<std::error_condition, unsigned> osd_get_cache_line_size() noexcept +{ + DWORD resultsize = 0; + if (GetLogicalProcessorInformation(nullptr, &resultsize) || (ERROR_INSUFFICIENT_BUFFER != GetLastError()) || !resultsize) + return std::make_pair(std::errc::operation_not_permitted, 0U); + + auto const result = reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION *>(std::malloc(resultsize)); + if (!result) + return std::make_pair(std::errc::not_enough_memory, 0U); + + if (!GetLogicalProcessorInformation(result, &resultsize)) + { + std::free(result); + return std::make_pair(std::errc::operation_not_permitted, 0U); + } + + for (unsigned i = 0; i < (resultsize / sizeof(result[0])); ++i) + { + if ((RelationCache == result[i].Relationship) && (1 == result[i].Cache.Level)) + { + unsigned const linesize = result[i].Cache.LineSize; + std::free(result); + return std::make_pair(std::error_condition(), linesize); + } + } + + std::free(result); + return std::make_pair(std::errc::operation_not_permitted, 0U); +} + + //============================================================ // get_clipboard_text_by_format //============================================================ diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index 90bb63bf7f5..24148889570 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -15,12 +15,12 @@ #include "modules/font/font_module.h" #include "modules/input/input_module.h" #include "modules/midi/midi_module.h" +#include "modules/netdev/netdev_module.h" #include "modules/monitor/monitor_module.h" #include "modules/netdev/netdev_module.h" #include "modules/render/render_module.h" #include "modules/sound/sound_module.h" -#include "osdnet.h" #include "watchdog.h" #include "emu.h" @@ -58,6 +58,7 @@ const options_entry osd_options::s_option_entries[] = { nullptr, nullptr, core_options::option_type::HEADER, "OSD DEBUGGING OPTIONS" }, { OSDOPTION_DEBUGGER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "debugger used: " }, + { OSDOPTION_DEBUGGER_HOST, "localhost", core_options::option_type::STRING, "address to bind to for gdbstub debugger" }, { OSDOPTION_DEBUGGER_PORT, "23946", core_options::option_type::INTEGER, "port to use for gdbstub debugger" }, { OSDOPTION_DEBUGGER_FONT ";dfont", OSDOPTVAL_AUTO, core_options::option_type::STRING, "font to use for debugger views" }, { OSDOPTION_DEBUGGER_FONT_SIZE ";dfontsize", "0", core_options::option_type::FLOAT, "font size to use for debugger views" }, @@ -207,6 +208,7 @@ osd_common_t::osd_common_t(osd_options &options) , m_sound(nullptr) , m_debugger(nullptr) , m_midi(nullptr) + , m_network(nullptr) , m_keyboard_input(nullptr) , m_mouse_input(nullptr) , m_lightgun_input(nullptr) @@ -269,6 +271,9 @@ void osd_common_t::register_options() #ifndef NO_USE_PULSEAUDIO REGISTER_MODULE(m_mod_man, SOUND_PULSEAUDIO); #endif +#ifndef NO_USE_PIPEWIRE + REGISTER_MODULE(m_mod_man, SOUND_PIPEWIRE); +#endif REGISTER_MODULE(m_mod_man, SOUND_NONE); REGISTER_MODULE(m_mod_man, MONITOR_SDL); @@ -308,6 +313,7 @@ void osd_common_t::register_options() REGISTER_MODULE(m_mod_man, MOUSEINPUT_WIN32); REGISTER_MODULE(m_mod_man, MOUSE_NONE); + REGISTER_MODULE(m_mod_man, LIGHTGUNINPUT_SDL); REGISTER_MODULE(m_mod_man, LIGHTGUN_X11); REGISTER_MODULE(m_mod_man, LIGHTGUNINPUT_RAWINPUT); REGISTER_MODULE(m_mod_man, LIGHTGUNINPUT_WIN32); @@ -462,9 +468,6 @@ void osd_common_t::update(bool skip_redraw) // if (m_watchdog != nullptr) m_watchdog->reset(); - - update_slider_list(); - } @@ -506,39 +509,58 @@ void osd_common_t::debugger_update() } -//------------------------------------------------- -// update_audio_stream - update the stereo audio -// stream -//------------------------------------------------- +bool osd_common_t::sound_external_per_channel_volume() +{ + return m_sound->external_per_channel_volume(); +} -void osd_common_t::update_audio_stream(const int16_t *buffer, int samples_this_frame) +bool osd_common_t::sound_split_streams_per_source() { - // - // This method is called whenever the system has new audio data to stream. - // It provides an array of stereo samples in L-R order which should be - // output at the configured sample_rate. - // - m_sound->update_audio_stream(m_machine->video().throttled(), buffer,samples_this_frame); + return m_sound->split_streams_per_source(); } +uint32_t osd_common_t::sound_get_generation() +{ + return m_sound->get_generation(); +} -//------------------------------------------------- -// set_mastervolume - set the system volume -//------------------------------------------------- +osd::audio_info osd_common_t::sound_get_information() +{ + return m_sound->get_information(); +} -void osd_common_t::set_mastervolume(int attenuation) +uint32_t osd_common_t::sound_stream_sink_open(uint32_t node, std::string name, uint32_t rate) { - // - // Attenuation is the attenuation in dB (a negative number). - // To convert from dB to a linear volume scale do the following: - // volume = MAX_VOLUME; - // while (attenuation++ < 0) - // volume /= 1.122018454; // = (10 ^ (1/20)) = 1dB - // - if (m_sound != nullptr) - m_sound->set_mastervolume(attenuation); + return m_sound->stream_sink_open(node, name, rate); } +uint32_t osd_common_t::sound_stream_source_open(uint32_t node, std::string name, uint32_t rate) +{ + return m_sound->stream_source_open(node, name, rate); +} + +void osd_common_t::sound_stream_set_volumes(uint32_t id, const std::vector<float> &db) +{ + m_sound->stream_set_volumes(id, db); +} + +void osd_common_t::sound_stream_close(uint32_t id) +{ + m_sound->stream_close(id); +} + +void osd_common_t::sound_stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) +{ + m_sound->stream_sink_update(id, buffer, samples_this_frame); +} + +void osd_common_t::sound_stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) +{ + m_sound->stream_source_update(id, buffer, samples_this_frame); +} + + + //------------------------------------------------- // customize_input_type_list - provide OSD @@ -567,6 +589,31 @@ void osd_common_t::customize_input_type_list(std::vector<input_type_entry> &type std::vector<ui::menu_item> osd_common_t::get_slider_list() { + // check if any window has dirty sliders + bool dirty = false; + for (const auto &window : window_list()) + { + if (window->has_renderer() && window->renderer().sliders_dirty()) + { + dirty = true; + break; + } + } + + if (dirty) + { + m_sliders.clear(); + + for (const auto &window : osd_common_t::window_list()) + { + if (window->has_renderer()) + { + std::vector<ui::menu_item> window_sliders = window->renderer().get_slider_list(); + m_sliders.insert(m_sliders.end(), window_sliders.begin(), window_sliders.end()); + } + } + } + return m_sliders; } @@ -591,17 +638,49 @@ bool osd_common_t::execute_command(const char *command) { if (strcmp(command, OSDCOMMAND_LIST_NETWORK_ADAPTERS) == 0) { - osd_module &om = select_module_options<osd_module>(OSD_NETDEV_PROVIDER); - osd_list_network_adapters(); - om.exit(); + auto &om = select_module_options<netdev_module>(OSD_NETDEV_PROVIDER); + auto const interfaces = om.list_devices(); + if (interfaces.empty()) + { + printf("No supported network interfaces were found\n"); + } + else + { + printf("Available network interfaces:\n"); + for (auto &entry : interfaces) + { + printf(" %.*s\n", int(entry.description.length()), entry.description.data()); + } + } + dynamic_cast<osd_module &>(om).exit(); return true; } else if (strcmp(command, OSDCOMMAND_LIST_MIDI_DEVICES) == 0) { - osd_module &om = select_module_options<osd_module>(OSD_MIDI_PROVIDER); - dynamic_cast<midi_module &>(om).list_midi_devices(); - om.exit(); + auto &om = select_module_options<midi_module>(OSD_MIDI_PROVIDER); + auto const ports = om.list_midi_ports(); + if (ports.empty()) + { + printf("No MIDI ports were found\n"); + } + else + { + printf("MIDI input ports:\n"); + for (auto const &port : ports) + { + if (port.input) + printf(port.default_input ? "%s (default)\n" : "%s\n", port.name.c_str()); + } + + printf("\nMIDI output ports:\n"); + for (auto const &port : ports) + { + if (port.output) + printf(port.default_output ? "%s (default)\n" : "%s\n", port.name.c_str()); + } + } + dynamic_cast<osd_module &>(om).exit(); return true; } @@ -641,10 +720,10 @@ void osd_common_t::init_subsystems() m_debugger = &select_module_options<debug_module>(OSD_DEBUG_PROVIDER); - select_module_options<netdev_module>(OSD_NETDEV_PROVIDER); - m_midi = &select_module_options<midi_module>(OSD_MIDI_PROVIDER); + m_network = &select_module_options<netdev_module>(OSD_NETDEV_PROVIDER); + m_output = &select_module_options<output_module>(OSD_OUTPUT_PROVIDER); machine().output().set_global_notifier(output_notifier_callback, this); @@ -717,7 +796,27 @@ bool osd_common_t::get_font_families(std::string const &font_path, std::vector<s return m_font_module->get_font_families(font_path, result); } -std::unique_ptr<osd_midi_device> osd_common_t::create_midi_device() +std::unique_ptr<osd::midi_input_port> osd_common_t::create_midi_input(std::string_view name) +{ + return m_midi->create_input(name); +} + +std::unique_ptr<osd::midi_output_port> osd_common_t::create_midi_output(std::string_view name) +{ + return m_midi->create_output(name); +} + +std::vector<osd::midi_port_info> osd_common_t::list_midi_ports() +{ + return m_midi->list_midi_ports(); +} + +std::unique_ptr<osd::network_device> osd_common_t::open_network_device(int id, osd::network_handler &handler) +{ + return m_network->open_device(id, handler); +} + +std::vector<osd::network_device_info> osd_common_t::list_network_devices() { - return m_midi->create_midi_device(); + return m_network->list_devices(); } diff --git a/src/osd/modules/lib/osdobj_common.h b/src/osd/modules/lib/osdobj_common.h index c5cd9b4fc49..ff0d0b9a6dd 100644 --- a/src/osd/modules/lib/osdobj_common.h +++ b/src/osd/modules/lib/osdobj_common.h @@ -43,6 +43,7 @@ #define OSDCOMMAND_LIST_NETWORK_ADAPTERS "listnetwork" #define OSDOPTION_DEBUGGER "debugger" +#define OSDOPTION_DEBUGGER_HOST "debugger_host" #define OSDOPTION_DEBUGGER_PORT "debugger_port" #define OSDOPTION_DEBUGGER_FONT "debugger_font" #define OSDOPTION_DEBUGGER_FONT_SIZE "debugger_font_size" @@ -119,6 +120,7 @@ public: // debugging options const char *debugger() const { return value(OSDOPTION_DEBUGGER); } + const char *debugger_host() const { return value(OSDOPTION_DEBUGGER_HOST); } int debugger_port() const { return int_value(OSDOPTION_DEBUGGER_PORT); } const char *debugger_font() const { return value(OSDOPTION_DEBUGGER_FONT); } float debugger_font_size() const { return float_value(OSDOPTION_DEBUGGER_FONT_SIZE); } @@ -195,6 +197,7 @@ class font_module; class input_module; class midi_module; class monitor_module; +class netdev_module; class osd_watchdog; class osd_window; class output_module; @@ -221,9 +224,17 @@ public: virtual void wait_for_debugger(device_t &device, bool firststop) override; // audio overridables - virtual void update_audio_stream(const int16_t *buffer, int samples_this_frame) override; - virtual void set_mastervolume(int attenuation) override; virtual bool no_sound() override; + virtual bool sound_external_per_channel_volume() override; + virtual bool sound_split_streams_per_source() override; + virtual uint32_t sound_get_generation() override; + virtual osd::audio_info sound_get_information() override; + virtual uint32_t sound_stream_sink_open(uint32_t node, std::string name, uint32_t rate) override; + virtual uint32_t sound_stream_source_open(uint32_t node, std::string name, uint32_t rate) override; + virtual void sound_stream_set_volumes(uint32_t id, const std::vector<float> &db) override; + virtual void sound_stream_close(uint32_t id) override; + virtual void sound_stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) override; + virtual void sound_stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) override; // input overridables virtual void customize_input_type_list(std::vector<input_type_entry> &typelist) override; @@ -232,13 +243,21 @@ public: virtual void add_audio_to_recording(const int16_t *buffer, int samples_this_frame) override; virtual std::vector<ui::menu_item> get_slider_list() override; + // font interface + virtual osd_font::ptr font_alloc() override; + virtual bool get_font_families(std::string const &font_path, std::vector<std::pair<std::string, std::string> > &result) override; + // command option overrides virtual bool execute_command(const char *command) override; - virtual osd_font::ptr font_alloc() override; - virtual bool get_font_families(std::string const &font_path, std::vector<std::pair<std::string, std::string> > &result) override; + // MIDI interface + virtual std::unique_ptr<osd::midi_input_port> create_midi_input(std::string_view name) override; + virtual std::unique_ptr<osd::midi_output_port> create_midi_output(std::string_view name) override; + virtual std::vector<osd::midi_port_info> list_midi_ports() override; - virtual std::unique_ptr<osd_midi_device> create_midi_device() override; + // network interface + virtual std::unique_ptr<osd::network_device> open_network_device(int id, osd::network_handler &handler) override; + virtual std::vector<osd::network_device_info> list_network_devices() override; // FIXME: everything below seems to be osd specific and not part of // this INTERFACE but part of the osd IMPLEMENTATION @@ -276,9 +295,6 @@ public: protected: virtual bool input_init(); - virtual void build_slider_list() { } - virtual void update_slider_list() { } - void poll_input_modules(bool relative_reset); static std::list<std::unique_ptr<osd_window> > s_window_list; @@ -316,6 +332,7 @@ protected: sound_module* m_sound; debug_module* m_debugger; midi_module* m_midi; + netdev_module* m_network; input_module* m_keyboard_input; input_module* m_mouse_input; input_module* m_lightgun_input; diff --git a/src/osd/modules/midi/midi_module.h b/src/osd/modules/midi/midi_module.h index 649d8f58b12..61eef311aa6 100644 --- a/src/osd/modules/midi/midi_module.h +++ b/src/osd/modules/midi/midi_module.h @@ -9,9 +9,12 @@ #pragma once -#include "osdepend.h" +#include "interface/midiport.h" #include <memory> +#include <string> +#include <string_view> +#include <vector> //============================================================ @@ -27,9 +30,9 @@ public: // specific routines - virtual std::unique_ptr<osd_midi_device> create_midi_device() = 0; - // FIXME: should return a list of strings ... - virtual void list_midi_devices() = 0; + virtual std::unique_ptr<osd::midi_input_port> create_input(std::string_view name) = 0; + virtual std::unique_ptr<osd::midi_output_port> create_output(std::string_view name) = 0; + virtual std::vector<osd::midi_port_info> list_midi_ports() = 0; }; #endif // MAME_OSD_MODULES_MIDI_MIDI_MODULE_H diff --git a/src/osd/modules/midi/none.cpp b/src/osd/modules/midi/none.cpp index 232a5f1e2a3..2a50bf71a84 100644 --- a/src/osd/modules/midi/none.cpp +++ b/src/osd/modules/midi/none.cpp @@ -20,18 +20,6 @@ namespace osd { namespace { -class osd_midi_device_none : public osd_midi_device -{ -public: - virtual bool open_input(const char *devname) override { return false; } - virtual bool open_output(const char *devname) override { return false; } - virtual void close() override { } - virtual bool poll() override { return false; } - virtual int read(uint8_t *pOut) override { return 0; } - virtual void write(uint8_t data) override { } -}; - - class none_module : public osd_module, public midi_module { public: @@ -40,20 +28,11 @@ public: virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual void exit() override { } - virtual std::unique_ptr<osd_midi_device> create_midi_device() override; - virtual void list_midi_devices() override; + virtual std::unique_ptr<midi_input_port> create_input(std::string_view name) override { return nullptr; } + virtual std::unique_ptr<midi_output_port> create_output(std::string_view name) override { return nullptr; } + virtual std::vector<osd::midi_port_info> list_midi_ports() override { return std::vector<osd::midi_port_info>(); } }; -std::unique_ptr<osd_midi_device> none_module::create_midi_device() -{ - return std::make_unique<osd_midi_device_none>(); -} - -void none_module::list_midi_devices() -{ - osd_printf_warning("\nMIDI is not supported in this configuration\n"); -} - } // anonymous namespace } // namespace osd diff --git a/src/osd/modules/midi/portmidi.cpp b/src/osd/modules/midi/portmidi.cpp index dba61b47c77..07372af1469 100644 --- a/src/osd/modules/midi/portmidi.cpp +++ b/src/osd/modules/midi/portmidi.cpp @@ -14,6 +14,9 @@ #ifndef NO_USE_MIDI +#include "interface/midiport.h" +#include "osdcore.h" // osd_printf_* + #include <portmidi.h> #include <cstdio> @@ -36,45 +39,51 @@ public: virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; - virtual std::unique_ptr<osd_midi_device> create_midi_device() override; - virtual void list_midi_devices() override; + virtual std::unique_ptr<midi_input_port> create_input(std::string_view name) override; + virtual std::unique_ptr<midi_output_port> create_output(std::string_view name) override; + virtual std::vector<osd::midi_port_info> list_midi_ports() override; }; -static const int RX_EVENT_BUF_SIZE = 512; +static constexpr unsigned RX_EVENT_BUF_SIZE = 512; -#define MIDI_SYSEX 0xf0 -#define MIDI_EOX 0xf7 +static constexpr uint8_t MIDI_SYSEX = 0xf0; +static constexpr uint8_t MIDI_EOX = 0xf7; -class osd_midi_device_pm : public osd_midi_device +class midi_input_pm : public midi_input_port { public: - osd_midi_device_pm(): pmStream(nullptr), xmit_cnt(0), last_status(0), rx_sysex(false) { } - virtual ~osd_midi_device_pm() { } - virtual bool open_input(const char *devname) override; - virtual bool open_output(const char *devname) override; - virtual void close() override; + midi_input_pm(PortMidiStream *stream) : m_stream(stream), m_rx_sysex(false) { } + virtual ~midi_input_pm(); + virtual bool poll() override; virtual int read(uint8_t *pOut) override; - virtual void write(uint8_t data) override; private: - PortMidiStream *pmStream; - PmEvent rx_evBuf[RX_EVENT_BUF_SIZE]; - uint8_t xmit_in[4]; // Pm_Messages mean we can at most have 3 residue bytes - int xmit_cnt; - uint8_t last_status; - bool rx_sysex; + PortMidiStream *const m_stream; + PmEvent m_evbuf[RX_EVENT_BUF_SIZE]; + bool m_rx_sysex; }; -std::unique_ptr<osd_midi_device> pm_module::create_midi_device() +class midi_output_pm : public midi_output_port { - return std::make_unique<osd_midi_device_pm>(); -} +public: + midi_output_pm(PortMidiStream *stream) : m_stream(stream), m_xmit_cnt(0), m_last_status(0) { } + virtual ~midi_output_pm(); + + virtual void write(uint8_t data) override; + +private: + PortMidiStream *const m_stream; + uint8_t m_xmit_in[4]; // Pm_Messages mean we can at most have 3 residue bytes + int m_xmit_cnt; + uint8_t m_last_status; +}; int pm_module::init(osd_interface &osd, const osd_options &options) { + // FIXME: check error return code Pm_Initialize(); return 0; } @@ -84,151 +93,126 @@ void pm_module::exit() Pm_Terminate(); } -void pm_module::list_midi_devices() +std::unique_ptr<midi_input_port> pm_module::create_input(std::string_view name) { - int num_devs = Pm_CountDevices(); - const PmDeviceInfo *pmInfo; - - printf("\n"); - - if (num_devs == 0) + int found_dev = -1; + if (name == "default") { - printf("No MIDI ports were found\n"); - return; + found_dev = Pm_GetDefaultInputDeviceID(); } - - printf("MIDI input ports:\n"); - for (int i = 0; i < num_devs; i++) + else { - pmInfo = Pm_GetDeviceInfo(i); - - if (pmInfo->input) + int const num_devs = Pm_CountDevices(); + for (found_dev = 0; num_devs > found_dev; ++found_dev) { - printf("%s %s\n", pmInfo->name, (i == Pm_GetDefaultInputDeviceID()) ? "(default)" : ""); + auto const info = Pm_GetDeviceInfo(found_dev); + if (info->input && (name == info->name)) + break; } + if (num_devs <= found_dev) + found_dev = -1; } - printf("\nMIDI output ports:\n"); - for (int i = 0; i < num_devs; i++) + if (0 > found_dev) { - pmInfo = Pm_GetDeviceInfo(i); - - if (pmInfo->output) - { - printf("%s %s\n", pmInfo->name, (i == Pm_GetDefaultOutputDeviceID()) ? "(default)" : ""); - } + osd_printf_warning("No MIDI input device named '%s' was found.\n", name); + return nullptr; } -} -bool osd_midi_device_pm::open_input(const char *devname) -{ - int num_devs = Pm_CountDevices(); - int found_dev = -1; - const PmDeviceInfo *pmInfo; - PortMidiStream *stm; - - if (!strcmp("default", devname)) + PortMidiStream *stream = nullptr; + PmError const err = Pm_OpenInput(&stream, found_dev, nullptr, RX_EVENT_BUF_SIZE, nullptr, nullptr); + if (pmNoError != err) { - found_dev = Pm_GetDefaultInputDeviceID(); - } - else - { - for (int i = 0; i < num_devs; i++) - { - pmInfo = Pm_GetDeviceInfo(i); - - if (pmInfo->input) - { - if (!strcmp(devname, pmInfo->name)) - { - found_dev = i; - break; - } - } - } + osd_printf_error("Error opening PortMidi device '%s' for input.\n", name); + return nullptr; } - if (found_dev >= 0) + try { - if (Pm_OpenInput(&stm, found_dev, nullptr, RX_EVENT_BUF_SIZE, nullptr, nullptr) == pmNoError) - { - pmStream = stm; - return true; - } - else - { - printf("Couldn't open PM device\n"); - return false; - } + return std::make_unique<midi_input_pm>(stream); } - else + catch (...) { - return false; + Pm_Close(stream); + return nullptr; } } -bool osd_midi_device_pm::open_output(const char *devname) +std::unique_ptr<midi_output_port> pm_module::create_output(std::string_view name) { - int num_devs = Pm_CountDevices(); int found_dev = -1; - const PmDeviceInfo *pmInfo; - PortMidiStream *stm; - - if (!strcmp("default", devname)) + if (name == "default") { found_dev = Pm_GetDefaultOutputDeviceID(); } else { - for (int i = 0; i < num_devs; i++) + int const num_devs = Pm_CountDevices(); + for (found_dev = 0; num_devs > found_dev; ++found_dev) { - pmInfo = Pm_GetDeviceInfo(i); - - if (pmInfo->output) - { - if (!strcmp(devname, pmInfo->name)) - { - found_dev = i; - break; - } - } + auto const info = Pm_GetDeviceInfo(found_dev); + if (info->output && (name == info->name)) + break; } + if (num_devs <= found_dev) + found_dev = -1; } - if (found_dev >= 0) + PortMidiStream *stream = nullptr; + PmError const err = Pm_OpenOutput(&stream, found_dev, nullptr, 100, nullptr, nullptr, 0); + if (pmNoError != err) { - if (Pm_OpenOutput(&stm, found_dev, nullptr, 100, nullptr, nullptr, 0) == pmNoError) - { - pmStream = stm; - return true; - } - else - { - printf("Couldn't open PM device\n"); - return false; - } + osd_printf_error("Error opening PortMidi device '%s' for output.\n", name); + return nullptr; } - else + + try + { + return std::make_unique<midi_output_pm>(stream); + } + catch (...) { - return false; + Pm_Close(stream); + return nullptr; } } -void osd_midi_device_pm::close() +std::vector<osd::midi_port_info> pm_module::list_midi_ports() { - Pm_Close(pmStream); + int const num_devs = Pm_CountDevices(); + int const def_input = Pm_GetDefaultInputDeviceID(); + int const def_output = Pm_GetDefaultOutputDeviceID(); + std::vector<osd::midi_port_info> result; + result.reserve(num_devs); + for (int i = 0; num_devs > i; ++i) + { + auto const pm_info = Pm_GetDeviceInfo(i); + result.emplace_back(osd::midi_port_info{ + pm_info->name, + 0 != pm_info->input, + 0 != pm_info->output, + def_input == i, + def_output == i }); + } + return result; } -bool osd_midi_device_pm::poll() + +midi_input_pm::~midi_input_pm() { - PmError chk = Pm_Poll(pmStream); + if (m_stream) + Pm_Close(m_stream); +} +bool midi_input_pm::poll() +{ + PmError const chk = Pm_Poll(m_stream); return (chk == pmGotData) ? true : false; } -int osd_midi_device_pm::read(uint8_t *pOut) +int midi_input_pm::read(uint8_t *pOut) { - int msgsRead = Pm_Read(pmStream, rx_evBuf, RX_EVENT_BUF_SIZE); + int msgsRead = Pm_Read(m_stream, m_evbuf, RX_EVENT_BUF_SIZE); int bytesOut = 0; if (msgsRead <= 0) @@ -238,23 +222,23 @@ int osd_midi_device_pm::read(uint8_t *pOut) for (int msg = 0; msg < msgsRead; msg++) { - uint8_t status = Pm_MessageStatus(rx_evBuf[msg].message); + uint8_t status = Pm_MessageStatus(m_evbuf[msg].message); - if (rx_sysex) + if (m_rx_sysex) { if (status & 0x80) // sys real-time imposing on us? { if (status == 0xf2) { *pOut++ = status; - *pOut++ = Pm_MessageData1(rx_evBuf[msg].message); - *pOut++ = Pm_MessageData2(rx_evBuf[msg].message); + *pOut++ = Pm_MessageData1(m_evbuf[msg].message); + *pOut++ = Pm_MessageData2(m_evbuf[msg].message); bytesOut += 3; } else if (status == 0xf3) { *pOut++ = status; - *pOut++ = Pm_MessageData1(rx_evBuf[msg].message); + *pOut++ = Pm_MessageData1(m_evbuf[msg].message); bytesOut += 2; } else @@ -263,7 +247,7 @@ int osd_midi_device_pm::read(uint8_t *pOut) bytesOut++; if (status == MIDI_EOX) { - rx_sysex = false; + m_rx_sysex = false; } } } @@ -271,15 +255,15 @@ int osd_midi_device_pm::read(uint8_t *pOut) { for (int i = 0; i < 4; i++) { - uint8_t byte = rx_evBuf[msg].message & 0xff; + uint8_t byte = m_evbuf[msg].message & 0xff; *pOut++ = byte; bytesOut++; if (byte == MIDI_EOX) { - rx_sysex = false; + m_rx_sysex = false; break; } - rx_evBuf[msg].message >>= 8; + m_evbuf[msg].message >>= 8; } } } @@ -290,7 +274,7 @@ int osd_midi_device_pm::read(uint8_t *pOut) case 0xc: // 2-byte messages case 0xd: *pOut++ = status; - *pOut++ = Pm_MessageData1(rx_evBuf[msg].message); + *pOut++ = Pm_MessageData1(m_evbuf[msg].message); bytesOut += 2; break; @@ -300,30 +284,30 @@ int osd_midi_device_pm::read(uint8_t *pOut) case 0: // System Exclusive { *pOut++ = status; // this should be OK: the shortest legal sysex is F0 tt dd F7, I believe - *pOut++ = (rx_evBuf[msg].message>>8) & 0xff; - *pOut++ = (rx_evBuf[msg].message>>16) & 0xff; - uint8_t last = *pOut++ = (rx_evBuf[msg].message>>24) & 0xff; + *pOut++ = (m_evbuf[msg].message>>8) & 0xff; + *pOut++ = (m_evbuf[msg].message>>16) & 0xff; + uint8_t last = *pOut++ = (m_evbuf[msg].message>>24) & 0xff; bytesOut += 4; - rx_sysex = (last != MIDI_EOX); + m_rx_sysex = (last != MIDI_EOX); break; } case 7: // End of System Exclusive *pOut++ = status; bytesOut += 1; - rx_sysex = false; + m_rx_sysex = false; break; case 2: // song pos *pOut++ = status; - *pOut++ = Pm_MessageData1(rx_evBuf[msg].message); - *pOut++ = Pm_MessageData2(rx_evBuf[msg].message); + *pOut++ = Pm_MessageData1(m_evbuf[msg].message); + *pOut++ = Pm_MessageData2(m_evbuf[msg].message); bytesOut += 3; break; case 3: // song select *pOut++ = status; - *pOut++ = Pm_MessageData1(rx_evBuf[msg].message); + *pOut++ = Pm_MessageData1(m_evbuf[msg].message); bytesOut += 2; break; @@ -336,8 +320,8 @@ int osd_midi_device_pm::read(uint8_t *pOut) default: *pOut++ = status; - *pOut++ = Pm_MessageData1(rx_evBuf[msg].message); - *pOut++ = Pm_MessageData2(rx_evBuf[msg].message); + *pOut++ = Pm_MessageData1(m_evbuf[msg].message); + *pOut++ = Pm_MessageData2(m_evbuf[msg].message); bytesOut += 3; break; } @@ -347,57 +331,64 @@ int osd_midi_device_pm::read(uint8_t *pOut) return bytesOut; } -void osd_midi_device_pm::write(uint8_t data) + +midi_output_pm::~midi_output_pm() +{ + if (m_stream) + Pm_Close(m_stream); +} + +void midi_output_pm::write(uint8_t data) { int bytes_needed = 0; PmEvent ev; ev.timestamp = 0; // use the current time -// printf("write: %02x (%d)\n", data, xmit_cnt); +// printf("write: %02x (%d)\n", data, m_xmit_cnt); // reject data bytes when no valid status exists - if ((last_status == 0) && !(data & 0x80)) + if ((m_last_status == 0) && !(data & 0x80)) { - xmit_cnt = 0; + m_xmit_cnt = 0; return; } - if (xmit_cnt >= 4) + if (m_xmit_cnt >= 4) { printf("MIDI out: packet assembly overflow, contact MAMEdev!\n"); return; } // handle sysex - if (last_status == MIDI_SYSEX) + if (m_last_status == MIDI_SYSEX) { -// printf("sysex: %02x (%d)\n", data, xmit_cnt); +// printf("sysex: %02x (%d)\n", data, m_xmit_cnt); // if we get a status that isn't sysex, assume it's system common if ((data & 0x80) && (data != MIDI_EOX)) { // printf("common during sysex!\n"); ev.message = Pm_Message(data, 0, 0); - Pm_Write(pmStream, &ev, 1); + Pm_Write(m_stream, &ev, 1); return; } - xmit_in[xmit_cnt++] = data; + m_xmit_in[m_xmit_cnt++] = data; // if EOX or 4 bytes filled, transmit 4 bytes - if ((xmit_cnt == 4) || (data == MIDI_EOX)) + if ((m_xmit_cnt == 4) || (data == MIDI_EOX)) { - ev.message = xmit_in[0] | (xmit_in[1]<<8) | (xmit_in[2]<<16) | (xmit_in[3]<<24); - Pm_Write(pmStream, &ev, 1); - xmit_in[0] = xmit_in[1] = xmit_in[2] = xmit_in[3] = 0; - xmit_cnt = 0; + ev.message = m_xmit_in[0] | (m_xmit_in[1]<<8) | (m_xmit_in[2]<<16) | (m_xmit_in[3]<<24); + Pm_Write(m_stream, &ev, 1); + m_xmit_in[0] = m_xmit_in[1] = m_xmit_in[2] = m_xmit_in[3] = 0; + m_xmit_cnt = 0; // printf("SysEx packet: %08x\n", ev.message); // if this is EOX, kill the running status if (data == MIDI_EOX) { - last_status = 0; + m_last_status = 0; } } @@ -405,33 +396,33 @@ void osd_midi_device_pm::write(uint8_t data) } // handle running status. don't allow system real-time messages to be considered as running status. - if ((xmit_cnt == 0) && (data & 0x80) && (data < 0xf8)) + if ((m_xmit_cnt == 0) && (data & 0x80) && (data < 0xf8)) { - last_status = data; + m_last_status = data; } - if ((xmit_cnt == 0) && !(data & 0x80)) + if ((m_xmit_cnt == 0) && !(data & 0x80)) { - xmit_in[xmit_cnt++] = last_status; - xmit_in[xmit_cnt++] = data; -// printf("\trunning status: [%d] = %02x, [%d] = %02x, last_status = %02x\n", xmit_cnt-2, last_status, xmit_cnt-1, data, last_status); + m_xmit_in[m_xmit_cnt++] = m_last_status; + m_xmit_in[m_xmit_cnt++] = data; +// printf("\trunning status: [%d] = %02x, [%d] = %02x, m_last_status = %02x\n", m_xmit_cnt-2, m_last_status, m_xmit_cnt-1, data, m_last_status); } else { - xmit_in[xmit_cnt++] = data; -// printf("\tNRS: [%d] = %02x\n", xmit_cnt-1, data); + m_xmit_in[m_xmit_cnt++] = data; +// printf("\tNRS: [%d] = %02x\n", m_xmit_cnt-1, data); } - if ((xmit_cnt == 1) && (xmit_in[0] == MIDI_SYSEX)) + if ((m_xmit_cnt == 1) && (m_xmit_in[0] == MIDI_SYSEX)) { // printf("Start SysEx!\n"); - last_status = MIDI_SYSEX; + m_last_status = MIDI_SYSEX; return; } // are we there yet? -// printf("status check: %02x\n", xmit_in[0]); - switch ((xmit_in[0]>>4) & 0xf) +// printf("status check: %02x\n", m_xmit_in[0]); + switch ((m_xmit_in[0]>>4) & 0xf) { case 0xc: // 2-byte messages case 0xd: @@ -439,7 +430,7 @@ void osd_midi_device_pm::write(uint8_t data) break; case 0xf: // system common - switch (xmit_in[0] & 0xf) + switch (m_xmit_in[0] & 0xf) { case 0: // System Exclusive is handled above break; @@ -464,11 +455,11 @@ void osd_midi_device_pm::write(uint8_t data) break; } - if (xmit_cnt == bytes_needed) + if (m_xmit_cnt == bytes_needed) { - ev.message = Pm_Message(xmit_in[0], xmit_in[1], xmit_in[2]); - Pm_Write(pmStream, &ev, 1); - xmit_cnt = 0; + ev.message = Pm_Message(m_xmit_in[0], m_xmit_in[1], m_xmit_in[2]); + Pm_Write(m_stream, &ev, 1); + m_xmit_cnt = 0; } } diff --git a/src/osd/modules/netdev/netdev_common.cpp b/src/osd/modules/netdev/netdev_common.cpp new file mode 100644 index 00000000000..b6a2328c7a6 --- /dev/null +++ b/src/osd/modules/netdev/netdev_common.cpp @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Carl + +#include "netdev_common.h" + + +namespace osd { + +network_device_base::network_device_base(network_handler &handler) + : m_handler(handler) + , m_stopped(true) +{ +} + +network_device_base::~network_device_base() +{ +} + +void network_device_base::start() +{ + m_stopped = false; +} + +void network_device_base::stop() +{ + m_stopped = true; +} + +void network_device_base::poll() +{ + uint8_t *buf; + int len; + while (!m_stopped && (len = recv_dev(&buf))) + { + m_handler.recv_cb(buf, len); + } +} + +} // namespace osd diff --git a/src/osd/modules/netdev/netdev_common.h b/src/osd/modules/netdev/netdev_common.h new file mode 100644 index 00000000000..95d961a438a --- /dev/null +++ b/src/osd/modules/netdev/netdev_common.h @@ -0,0 +1,36 @@ +// license:BSD-3-Clause +// copyright-holders:Carl +#ifndef MAME_OSD_NETDEV_NETDEV_COMMON_H +#define MAME_OSD_NETDEV_NETDEV_COMMON_H + +#pragma once + +#include "interface/nethandler.h" + +#include <cstdint> + + +namespace osd { + +class network_device_base : public network_device +{ +public: + virtual ~network_device_base(); + + virtual void start() override; + virtual void stop() override; + virtual void poll() override; + +protected: + network_device_base(network_handler &handler); + + virtual int recv_dev(uint8_t **buf) = 0; + +private: + network_handler &m_handler; + bool m_stopped; +}; + +} // namespace osd + +#endif // MAME_OSD_NETDEV_NETDEV_COMMON_H diff --git a/src/osd/modules/netdev/netdev_module.h b/src/osd/modules/netdev/netdev_module.h index b102e56fbb8..3636c3a23cb 100644 --- a/src/osd/modules/netdev/netdev_module.h +++ b/src/osd/modules/netdev/netdev_module.h @@ -9,6 +9,11 @@ #pragma once +#include "interface/nethandler.h" + +#include <memory> +#include <vector> + //============================================================ // CONSTANTS @@ -20,7 +25,9 @@ class netdev_module { public: virtual ~netdev_module() = default; - // no specific routines below ... may change + + virtual std::unique_ptr<osd::network_device> open_device(int id, osd::network_handler &handler) = 0; + virtual std::vector<osd::network_device_info> list_devices() = 0; }; #endif // MAME_OSD_NETDEV_NETDEV_MODULE_H diff --git a/src/osd/modules/netdev/none.cpp b/src/osd/modules/netdev/none.cpp index 8441c002802..284ff51a962 100644 --- a/src/osd/modules/netdev/none.cpp +++ b/src/osd/modules/netdev/none.cpp @@ -8,6 +8,9 @@ #include "modules/osdmodule.h" +#include <memory> +#include <vector> + namespace osd { @@ -21,7 +24,21 @@ public: } virtual ~netdev_none() { } - virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } + + virtual int init(osd_interface &osd, const osd_options &options) override + { + return 0; + } + + virtual std::unique_ptr<network_device> open_device(int id, network_handler &handler) override + { + return std::unique_ptr<network_device>(); + } + + virtual std::vector<network_device_info> list_devices() override + { + return std::vector<network_device_info>(); + } }; } // anonymous namespace diff --git a/src/osd/modules/netdev/pcap.cpp b/src/osd/modules/netdev/pcap.cpp index bf54f450ac8..8ad62773d58 100644 --- a/src/osd/modules/netdev/pcap.cpp +++ b/src/osd/modules/netdev/pcap.cpp @@ -7,19 +7,25 @@ #if defined(OSD_NET_USE_PCAP) -#include "emu.h" -#include "dinetwork.h" -#include "osdnet.h" +#include "netdev_common.h" + #include "modules/lib/osdlib.h" +#include "osdcore.h" // osd_printf_* + +#include "util/strformat.h" // string_format + +#include <memory> +#include <vector> + #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) #include <windows.h> #undef interface #define LIB_NAME "wpcap.dll" #elif defined(SDLMAME_MACOSX) +#include <atomic> #include <pthread.h> -#include <libkern/OSAtomic.h> #define LIB_NAME "libpcap.dylib" #else @@ -46,10 +52,10 @@ typedef int (*pcap_dispatch_fn)(pcap_t *, int, pcap_handler, u_char *); class pcap_module : public osd_module, public netdev_module { public: - pcap_module() - : osd_module(OSD_NETDEV_PROVIDER, "pcap"), netdev_module(), - pcap_findalldevs_dl(nullptr), pcap_open_live_dl(nullptr), pcap_next_ex_dl(nullptr), pcap_compile_dl(nullptr), - pcap_close_dl(nullptr), pcap_setfilter_dl(nullptr), pcap_sendpacket_dl(nullptr), pcap_set_datalink_dl(nullptr), pcap_dispatch_dl(nullptr) + pcap_module() : + osd_module(OSD_NETDEV_PROVIDER, "pcap"), netdev_module(), + pcap_findalldevs_dl(nullptr), pcap_open_live_dl(nullptr), pcap_next_ex_dl(nullptr), pcap_compile_dl(nullptr), + pcap_close_dl(nullptr), pcap_setfilter_dl(nullptr), pcap_sendpacket_dl(nullptr), pcap_set_datalink_dl(nullptr), pcap_dispatch_dl(nullptr) { } @@ -83,6 +89,20 @@ public: return true; } + virtual std::unique_ptr<network_device> open_device(int id, network_handler &handler) override; + virtual std::vector<network_device_info> list_devices() override; + +private: + struct device_info + { + std::string name; + std::string description; + }; + + class netdev_pcap; + + std::vector<device_info> m_devices; + osd::dynamic_module::ptr pcap_dll; pcap_findalldevs_fn pcap_findalldevs_dl; @@ -96,191 +116,161 @@ public: pcap_dispatch_fn pcap_dispatch_dl; }; -// FIXME: bridge between pcap_module and netdev_pcap -static pcap_module *module = nullptr; - -#ifdef SDLMAME_MACOSX -struct netdev_pcap_context { - uint8_t *pkt; - int len; - pcap_t *p; - - uint8_t packets[32][1600]; - int packetlens[32]; - int head; - int tail; -}; -#endif - -class netdev_pcap : public osd_netdev +class pcap_module::netdev_pcap : public network_device_base { public: - netdev_pcap(const char *name, class device_network_interface *ifdev, int rate); + netdev_pcap(pcap_module &module, const char *name, network_handler &handler); ~netdev_pcap(); - virtual int send(uint8_t *buf, int len) override; - virtual void set_mac(const char *mac) override; + virtual int send(void const *buf, int len) override; + protected: virtual int recv_dev(uint8_t **buf) override; + private: + pcap_module &m_module; pcap_t *m_p; #ifdef SDLMAME_MACOSX - struct netdev_pcap_context m_ctx; pthread_t m_thread; + uint8_t *pkt; + int len; + pcap_t *p; + uint8_t m_pktbuf[2048]; + + uint8_t m_packets[32][1600]; + int m_packetlens[32]; + std::atomic<int> m_head; + std::atomic<int> m_tail; + + static void pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes); + static void *pcap_blocker(void *arg); #endif }; #ifdef SDLMAME_MACOSX -static void netdev_pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { - struct netdev_pcap_context *ctx = (struct netdev_pcap_context*)user; +void pcap_module::netdev_pcap::pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) +{ + netdev_pcap *const ctx = reinterpret_cast<netdev_pcap *>(user); - if(!ctx->p) return; + if (!ctx->p) + return; - if(OSAtomicCompareAndSwapInt((ctx->head+1) & 0x1F, ctx->tail, &ctx->tail)) { + int const curr = ctx->m_head.load(std::memory_order_relaxed); + int const next = (curr + 1) & 0x1f; + if (ctx->m_tail.load(std::memory_order_acquire) == next) + { printf("buffer full, dropping packet\n"); return; } - memcpy(ctx->packets[ctx->head], bytes, h->len); - ctx->packetlens[ctx->head] = h->len; - OSAtomicCompareAndSwapInt(ctx->head, (ctx->head+1) & 0x1F, &ctx->head); + memcpy(ctx->m_packets[curr], bytes, h->len); + ctx->m_packetlens[curr] = h->len; + ctx->m_head.store(next, std::memory_order_release); } -static void *netdev_pcap_blocker(void *arg) { - struct netdev_pcap_context *ctx = (struct netdev_pcap_context*)arg; +void *pcap_module::netdev_pcap::pcap_blocker(void *arg) +{ + netdev_pcap *const ctx = reinterpret_cast<netdev_pcap *>(arg); - while(ctx && ctx->p) { - (*module->pcap_dispatch_dl)(ctx->p, 1, netdev_pcap_handler, (u_char*)ctx); - } + while (ctx && ctx->p) + (*ctx->m_module.pcap_dispatch_dl)(ctx->p, 1, &netdev_pcap::pcap_handler, reinterpret_cast<u_char *>(ctx)); - return 0; + return nullptr; } #endif -netdev_pcap::netdev_pcap(const char *name, class device_network_interface *ifdev, int rate) - : osd_netdev(ifdev, rate) +pcap_module::netdev_pcap::netdev_pcap(pcap_module &module, const char *name, network_handler &handler) : + network_device_base(handler), + m_module(module), + m_p(nullptr) { char errbuf[PCAP_ERRBUF_SIZE]; #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) - m_p = (*module->pcap_open_live_dl)(name, 65535, 1, -1, errbuf); + m_p = (*m_module.pcap_open_live_dl)(name, 65535, 1, -1, errbuf); #else - m_p = (*module->pcap_open_live_dl)(name, 65535, 1, 1, errbuf); + m_p = (*m_module.pcap_open_live_dl)(name, 65535, 1, 1, errbuf); #endif if(!m_p) { osd_printf_error("Unable to open %s: %s\n", name, errbuf); return; } - if ((*module->pcap_set_datalink_dl)(m_p, DLT_EN10MB) == -1) + if ((*m_module.pcap_set_datalink_dl)(m_p, DLT_EN10MB) == -1) { osd_printf_error("Unable to set %s to ethernet", name); - (*module->pcap_close_dl)(m_p); + (*m_module.pcap_close_dl)(m_p); m_p = nullptr; return; } - netdev_pcap::set_mac(get_mac()); #ifdef SDLMAME_MACOSX - m_ctx.head = 0; - m_ctx.tail = 0; - m_ctx.p = m_p; - pthread_create(&m_thread, nullptr, netdev_pcap_blocker, &m_ctx); + m_head = 0; + m_tail = 0; + p = m_p; + pthread_create(&m_thread, nullptr, &netdev_pcap::pcap_blocker, this); #endif } -void netdev_pcap::set_mac(const char *mac) +int pcap_module::netdev_pcap::send(void const *buf, int len) { - struct bpf_program fp; - if(!m_p) return; -#ifdef SDLMAME_MACOSX - auto filter = util::string_format("not ether src %02X:%02X:%02X:%02X:%02X:%02X and (ether dst %02X:%02X:%02X:%02X:%02X:%02X or ether multicast or ether broadcast or ether dst 09:00:07:ff:ff:ff)", (unsigned char)mac[0], (unsigned char)mac[1], (unsigned char)mac[2],(unsigned char)mac[3], (unsigned char)mac[4], (unsigned char)mac[5], (unsigned char)mac[0], (unsigned char)mac[1], (unsigned char)mac[2],(unsigned char)mac[3], (unsigned char)mac[4], (unsigned char)mac[5]); -#else - auto filter = util::string_format("ether dst %02X:%02X:%02X:%02X:%02X:%02X or ether multicast or ether broadcast", (unsigned char)mac[0], (unsigned char)mac[1], (unsigned char)mac[2],(unsigned char)mac[3], (unsigned char)mac[4], (unsigned char)mac[5]); -#endif - if ((*module->pcap_compile_dl)(m_p, &fp, filter.c_str(), 1, 0) == -1) { - osd_printf_error("Error with pcap_compile\n"); - } - if ((*module->pcap_setfilter_dl)(m_p, &fp) == -1) { - osd_printf_error("Error with pcap_setfilter\n"); - } -} - -int netdev_pcap::send(uint8_t *buf, int len) -{ - int ret; - if(!m_p) { + if (!m_p) + { printf("send invoked, but no pcap context\n"); return 0; } - ret = (*module->pcap_sendpacket_dl)(m_p, buf, len); + int ret = (*m_module.pcap_sendpacket_dl)(m_p, reinterpret_cast<const u_char *>(buf), len); printf("sent packet length %d, returned %d\n", len, ret); return ret ? len : 0; - //return (!pcap_sendpacket_dl(m_p, buf, len))?len:0; + //return (!pcap_sendpacket_dl(m_p, reinterpret_cast<const u_char *>(buf), len)) ? len : 0; } -int netdev_pcap::recv_dev(uint8_t **buf) +int pcap_module::netdev_pcap::recv_dev(uint8_t **buf) { -#ifdef SDLMAME_MACOSX - uint8_t pktbuf[2048]; - int ret; - // no device open? - if(!m_p) return 0; + if (!m_p) + return 0; +#ifdef SDLMAME_MACOSX // Empty - if(OSAtomicCompareAndSwapInt(m_ctx.head, m_ctx.tail, &m_ctx.tail)) { + int const curr = m_tail.load(std::memory_order_relaxed); + if (m_head.load(std::memory_order_acquire) == curr) return 0; - } - memcpy(pktbuf, m_ctx.packets[m_ctx.tail], m_ctx.packetlens[m_ctx.tail]); - ret = m_ctx.packetlens[m_ctx.tail]; - OSAtomicCompareAndSwapInt(m_ctx.tail, (m_ctx.tail+1) & 0x1F, &m_ctx.tail); - *buf = pktbuf; + memcpy(m_pktbuf, m_packets[curr], m_packetlens[curr]); + int ret = m_packetlens[curr]; + m_tail.store((curr + 1) & 0x1f, std::memory_order_release); + *buf = m_pktbuf; return ret; #else struct pcap_pkthdr *header; - if(!m_p) return 0; - return ((*module->pcap_next_ex_dl)(m_p, &header, (const u_char **)buf) == 1)?header->len:0; + return ((*m_module.pcap_next_ex_dl)(m_p, &header, (const u_char **)buf) == 1)?header->len:0; #endif } -netdev_pcap::~netdev_pcap() +pcap_module::netdev_pcap::~netdev_pcap() { #ifdef SDLMAME_MACOSX - m_ctx.p = nullptr; + p = nullptr; pthread_cancel(m_thread); pthread_join(m_thread, nullptr); #endif - if(m_p) (*module->pcap_close_dl)(m_p); + if(m_p) (*m_module.pcap_close_dl)(m_p); m_p = nullptr; } -static CREATE_NETDEV(create_pcap) -{ - auto *dev = new netdev_pcap(ifname, ifdev, rate); - return dynamic_cast<osd_netdev *>(dev); -} - int pcap_module::init(osd_interface &osd, const osd_options &options) { pcap_if_t *devs; char errbuf[PCAP_ERRBUF_SIZE]; - // FIXME: bridge between pcap_module and netdev_pcap - module = this; - if ((*pcap_findalldevs_dl)(&devs, errbuf) == -1) { osd_printf_error("Unable to get network devices: %s\n", errbuf); return 1; } - while(devs) + while (devs) { - if(devs->description) { - add_netdev(devs->name, devs->description, create_pcap); - } else { - add_netdev(devs->name, devs->name, create_pcap); - } + m_devices.emplace_back(device_info{ devs->name, devs->description ? devs->description : devs->name }); devs = devs->next; } return 0; @@ -288,7 +278,24 @@ int pcap_module::init(osd_interface &osd, const osd_options &options) void pcap_module::exit() { - clear_netdev(); + m_devices.clear(); +} + +std::unique_ptr<network_device> pcap_module::open_device(int id, network_handler &handler) +{ + if ((0 > id) || (m_devices.size() <= id)) + return nullptr; + + return std::make_unique<netdev_pcap>(*this, m_devices[id].name.c_str(), handler); +} + +std::vector<network_device_info> pcap_module::list_devices() +{ + std::vector<network_device_info> result; + result.reserve(m_devices.size()); + for (int id = 0; m_devices.size() > id; ++id) + result.emplace_back(network_device_info{ id, m_devices[id].description }); + return result; } } // anonymous namespace diff --git a/src/osd/modules/netdev/taptun.cpp b/src/osd/modules/netdev/taptun.cpp index 5196b6e7d2f..b765a667ae7 100644 --- a/src/osd/modules/netdev/taptun.cpp +++ b/src/osd/modules/netdev/taptun.cpp @@ -6,6 +6,17 @@ #if defined(OSD_NET_USE_TAPTUN) +#include "netdev_common.h" + +#include "osdcore.h" // osd_printf_verbose +#include "osdfile.h" // PATH_SEPARATOR + +#include "util/hashing.h" // crc32_creator +#include "util/unicode.h" + +#include <memory> +#include <vector> + #if defined(_WIN32) #include <windows.h> #include <winioctl.h> @@ -17,11 +28,6 @@ #include <cerrno> #endif -#include "emu.h" -#include "dinetwork.h" -#include "osdnet.h" -#include "unicode.h" - #ifdef __linux__ #define IFF_TAP 0x0002 #define IFF_NO_PI 0x1000 @@ -50,24 +56,37 @@ public: } virtual ~taptun_module() { } - virtual int init(osd_interface &osd, const osd_options &options); - virtual void exit(); + virtual int init(osd_interface &osd, const osd_options &options) override; + virtual void exit() override; - virtual bool probe() { return true; } + virtual bool probe() override { return true; } + + virtual std::unique_ptr<network_device> open_device(int id, network_handler &handler) override; + virtual std::vector<network_device_info> list_devices() override; + +private: + struct device_info + { + std::string name; + std::string description; + }; + + std::vector<device_info> m_devices; }; -class netdev_tap : public osd_netdev +class netdev_tap : public network_device_base { public: - netdev_tap(const char *name, class device_network_interface *ifdev, int rate); + netdev_tap(const char *name, network_handler &handler); ~netdev_tap(); - int send(uint8_t *buf, int len) override; - void set_mac(const char *mac) override; + int send(void const *buf, int len) override; + protected: int recv_dev(uint8_t **buf) override; + private: #if defined(_WIN32) HANDLE m_handle = INVALID_HANDLE_VALUE; @@ -77,22 +96,20 @@ private: int m_fd = -1; char m_ifname[10]; #endif - char m_mac[6]; uint8_t m_buf[2048]; }; -netdev_tap::netdev_tap(const char *name, class device_network_interface *ifdev, int rate) - : osd_netdev(ifdev, rate) +netdev_tap::netdev_tap(const char *name, network_handler &handler) + : network_device_base(handler) { -#ifdef __linux__ - struct ifreq ifr; - +#if defined(__linux__) m_fd = -1; if((m_fd = open("/dev/net/tun", O_RDWR)) == -1) { osd_printf_verbose("tap: open failed %d\n", errno); return; } + struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI; sprintf(ifr.ifr_name, "tap-mess-%d-0", getuid()); @@ -141,11 +158,6 @@ netdev_tap::~netdev_tap() #endif } -void netdev_tap::set_mac(const char *mac) -{ - memcpy(m_mac, mac, 6); -} - static u32 finalise_frame(u8 buf[], u32 length) { /* @@ -177,7 +189,7 @@ static u32 finalise_frame(u8 buf[], u32 length) } #if defined(_WIN32) -int netdev_tap::send(uint8_t *buf, int len) +int netdev_tap::send(void const *buf, int len) { OVERLAPPED overlapped = {}; @@ -241,7 +253,7 @@ static std::wstring safe_string(WCHAR value[], int length) } // find the friendly name for an adapter in the registry -static std::wstring get_connection_name(std::wstring &id) +static std::wstring get_connection_name(std::wstring const &id) { std::wstring result; @@ -323,7 +335,7 @@ static std::vector<std::wstring> get_tap_adapters() return result; } #else -int netdev_tap::send(uint8_t *buf, int len) +int netdev_tap::send(void const *buf, int len) { if(m_fd == -1) return 0; len = write(m_fd, buf, len); @@ -332,42 +344,52 @@ int netdev_tap::send(uint8_t *buf, int len) int netdev_tap::recv_dev(uint8_t **buf) { - int len; - if(m_fd == -1) return 0; - // exit if we didn't receive anything, got an error, got a broadcast or multicast packet, - // are in promiscuous mode or got a packet with our mac. - do { - len = read(m_fd, m_buf, sizeof(m_buf)); - } while((len > 0) && memcmp(get_mac(), m_buf, 6) && !get_promisc() && !(m_buf[0] & 1)); + if (0 > m_fd) + return 0; + + int len = read(m_fd, m_buf, sizeof(m_buf)); if (len > 0) len = finalise_frame(m_buf, len); *buf = m_buf; - return (len == -1)?0:len; + return (len == -1) ? 0 : len; } #endif -static CREATE_NETDEV(create_tap) -{ - auto *dev = new netdev_tap(ifname, ifdev, rate); - return dynamic_cast<osd_netdev *>(dev); -} - int taptun_module::init(osd_interface &osd, const osd_options &options) { #if defined(_WIN32) - for (std::wstring &id : get_tap_adapters()) - add_netdev(utf8_from_wstring(id).c_str(), utf8_from_wstring(get_connection_name(id)).c_str(), create_tap); + auto const adapters = get_tap_adapters(); + m_devices.reserve(adapters.size()); + for (std::wstring const &id : adapters) + m_devices.emplace_back(device_info{ utf8_from_wstring(id), utf8_from_wstring(get_connection_name(id)) }); #else - add_netdev("tap", "TAP/TUN Device", create_tap); + m_devices.emplace_back(device_info{ "tap", "TUN/TAP Device" }); #endif return 0; } void taptun_module::exit() { - clear_netdev(); + m_devices.clear(); +} + +std::unique_ptr<network_device> taptun_module::open_device(int id, network_handler &handler) +{ + if ((0 > id) || (m_devices.size() <= id)) + return nullptr; + + return std::make_unique<netdev_tap>(m_devices[id].name.c_str(), handler); +} + +std::vector<network_device_info> taptun_module::list_devices() +{ + std::vector<network_device_info> result; + result.reserve(m_devices.size()); + for (int id = 0; m_devices.size() > id; ++id) + result.emplace_back(network_device_info{ id, m_devices[id].description }); + return result; } } // anonymous namespace diff --git a/src/osd/modules/opengl/gl_shader_tool.h b/src/osd/modules/opengl/gl_shader_tool.h index 28a84143b29..50845bd590f 100644 --- a/src/osd/modules/opengl/gl_shader_tool.h +++ b/src/osd/modules/opengl/gl_shader_tool.h @@ -7,7 +7,8 @@ * Copyright (c) 2007, Sven Gothel * Copyright (c) 2007, MAME Team * - * GPL version 2 + * May be distributed under the terms of the GNU General Public + * License, version 2.0, or the 3-Clause BSD License. * * OpenGL GLSL ARB extensions: * diff --git a/src/osd/modules/osdwindow.h b/src/osd/modules/osdwindow.h index f3d39878b5e..9167a775682 100644 --- a/src/osd/modules/osdwindow.h +++ b/src/osd/modules/osdwindow.h @@ -15,6 +15,8 @@ #include "osdhelper.h" #include "../frontend/mame/ui/menuitem.h" +#include <cassert> +#include <chrono> #include <memory> #include <string> #include <vector> @@ -94,6 +96,65 @@ public: virtual void complete_destroy() = 0; protected: + static inline constexpr int CLICK_DISTANCE = 16; // in pointer units squared + static inline constexpr int TAP_DISTANCE = 49; // taps are less repeatable than clicks + + struct prev_touch + { + prev_touch() = default; + prev_touch(prev_touch const &) = default; + prev_touch(prev_touch &&) = default; + prev_touch &operator=(prev_touch const &) = default; + prev_touch &operator=(prev_touch &&) = default; + + std::chrono::steady_clock::time_point when = std::chrono::steady_clock::time_point::min(); + int x = 0, y = 0, cnt = 0; + }; + + struct pointer_dev_info + { + pointer_dev_info() = default; + pointer_dev_info(pointer_dev_info const &) = default; + pointer_dev_info(pointer_dev_info &&) = default; + pointer_dev_info &operator=(pointer_dev_info const &) = default; + pointer_dev_info &operator=(pointer_dev_info &&) = default; + + unsigned clear_expired_touches(std::chrono::steady_clock::time_point const &now, std::chrono::steady_clock::duration const &time); + unsigned consume_touch(unsigned i); + + prev_touch touches[8]; + }; + + struct pointer_info + { + pointer_info(pointer_info const &) = default; + pointer_info(pointer_info &&) = default; + pointer_info &operator=(pointer_info const &) = default; + pointer_info &operator=(pointer_info &&) = default; + + pointer_info(unsigned i, unsigned d); + + void primary_down( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance, + bool checkprev, + std::vector<pointer_dev_info> &devices); + void check_primary_hold_drag( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance); + + std::chrono::steady_clock::time_point pressed; + unsigned index, device; + int x, y; + unsigned buttons; + int pressedx, pressedy; + int clickcnt; + }; + osd_window( running_machine &machine, render_module &renderprovider, @@ -127,6 +188,140 @@ private: const std::string m_title; }; + +inline unsigned osd_window::pointer_dev_info::clear_expired_touches( + std::chrono::steady_clock::time_point const &now, + std::chrono::steady_clock::duration const &time) +{ + // find first non-expired touch (if any) + unsigned end(0); + while ((std::size(touches) > end) && touches[end].cnt && ((touches[end].when + time) < now)) + touches[end++].cnt = 0; + + // shift non-expired touches back if necessary + if (end) + { + unsigned pos(0); + while ((std::size(touches) > end) && touches[end].cnt) + { + touches[pos++] = std::move(touches[end]); + touches[end++].cnt = 0; + } + return pos; + } + else + { + while ((std::size(touches) > end) && touches[end].cnt) + ++end; + return end; + } +} + +inline unsigned osd_window::pointer_dev_info::consume_touch(unsigned i) +{ + assert(std::size(touches) > i); + touches[i].cnt = 0; + unsigned pos(i + 1); + while ((std::size(touches) > pos) && touches[pos].cnt) + { + touches[i++] = std::move(touches[pos]); + touches[pos++].cnt = 0; + } + return i; +} + +inline osd_window::pointer_info::pointer_info(unsigned i, unsigned d) : + pressed(std::chrono::steady_clock::time_point::min()), + index(i), + device(d), + x(-1), + y(-1), + buttons(0), + pressedx(0), + pressedy(0), + clickcnt(0) +{ +} + +inline void osd_window::pointer_info::primary_down( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance, + bool checkprev, + std::vector<pointer_dev_info> &devices) +{ + auto const now(std::chrono::steady_clock::now()); + auto const exp(time + pressed); + if (0 > clickcnt) + { + // previous click turned into a hold/drag + clickcnt = 1; + } + else if (clickcnt) + { + // potential multi-click action + int const dx(cx - pressedx); + int const dy(cy - pressedy); + int const distance((dx * dx) + (dy * dy)); + if ((exp < now) || (tolerance < distance)) + clickcnt = 1; + else + ++clickcnt; + } + else + { + // first click for this pointer, but may need to check previous touches + clickcnt = 1; + if (checkprev) + { + if (devices.size() > device) + { + auto &devinfo(devices[device]); + unsigned const end(devinfo.clear_expired_touches(now, time)); + for (int i = 0; end > i; ++i) + { + int const dx(cx - devinfo.touches[i].x); + int const dy(cy - devinfo.touches[i].y); + int const distance((dx * dx) + (dy * dy)); + if (tolerance >= distance) + { + // close match - consume it + clickcnt = devinfo.touches[i].cnt + 1; + devinfo.consume_touch(i); + break; + } + } + } + } + } + + // record the time/location where the pointer was pressed + pressed = now; + pressedx = cx; + pressedy = cy; +} + +inline void osd_window::pointer_info::check_primary_hold_drag( + int cx, + int cy, + std::chrono::steady_clock::duration const &time, + int tolerance) +{ + // check for conversion to a (multi-)click-and-hold/drag + if (0 < clickcnt) + { + auto const now(std::chrono::steady_clock::now()); + auto const exp(time + pressed); + int const dx(cx - pressedx); + int const dy(cy - pressedy); + int const distance((dx * dx) + (dy * dy)); + if ((exp < now) || (tolerance < distance)) + clickcnt = -clickcnt; + } +} + + template <class TWindowHandle> class osd_window_t : public osd_window { diff --git a/src/osd/modules/render/aviwrite.cpp b/src/osd/modules/render/aviwrite.cpp index e86d8f99ba6..e758d467e37 100644 --- a/src/osd/modules/render/aviwrite.cpp +++ b/src/osd/modules/render/aviwrite.cpp @@ -72,7 +72,7 @@ void avi_write::begin_avi_recording(std::string_view name) info.audio_timescale = m_machine.sample_rate(); info.audio_sampletime = 1; info.audio_numsamples = 0; - info.audio_channels = 2; + info.audio_channels = m_machine.sound().outputs_count(); info.audio_samplebits = 16; info.audio_samplerate = m_machine.sample_rate(); @@ -142,9 +142,10 @@ void avi_write::audio_frame(const int16_t *buffer, int samples_this_frame) if (m_output_file != nullptr) { // write the next frame - avi_file::error avierr = m_output_file->append_sound_samples(0, buffer + 0, samples_this_frame, 1); - if (avierr == avi_file::error::NONE) - avierr = m_output_file->append_sound_samples(1, buffer + 1, samples_this_frame, 1); + int channels = m_machine.sound().outputs_count(); + avi_file::error avierr = avi_file::error::NONE; + for (int channel = 0; channel != channels && avierr == avi_file::error::NONE; channel ++) + avierr = m_output_file->append_sound_samples(channel, buffer + channel, samples_this_frame, channels-1); if (avierr != avi_file::error::NONE) { osd_printf_error("Error while logging AVI audio frame: %s\n", avi_file::error_string(avierr)); diff --git a/src/osd/modules/render/bgfx/shadermanager.cpp b/src/osd/modules/render/bgfx/shadermanager.cpp index 763a744e4aa..282029cb96a 100644 --- a/src/osd/modules/render/bgfx/shadermanager.cpp +++ b/src/osd/modules/render/bgfx/shadermanager.cpp @@ -83,6 +83,15 @@ std::string shader_manager::make_path_string(const osd_options &options, const s { std::string shader_path(options.bgfx_path()); shader_path += PATH_SEPARATOR "shaders" PATH_SEPARATOR; + +#if defined(SDLMAME_EMSCRIPTEN) + // Hard-code renderer type to OpenGL ES for emscripten builds since the + // bgfx::getRendererType() is called here before BGFX has been + // initialized and therefore gives the wrong renderer type (Noop). + shader_path += "essl" PATH_SEPARATOR; + return shader_path; +#endif + switch (bgfx::getRendererType()) { case bgfx::RendererType::Noop: diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/fs_xbr-lv3-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/fs_xbr-lv3-pass0.sc index 4331c2353f6..229f2f93936 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/fs_xbr-lv3-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/fs_xbr-lv3-pass0.sc @@ -120,7 +120,7 @@ vec4 weighted_distance(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, v LEFT = UP = LEFT3 = UP3 = 0.0; \ PX0 = vec2(0.0, PX); \ LIN0 = vec4(0.0, 0.0, 0.0, 0.0); \ - } \ + } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/fs_xbr-mlv4-pass4.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/fs_xbr-mlv4-pass4.sc index c37c512a7f6..bdd9f5b31db 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/fs_xbr-mlv4-pass4.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/fs_xbr-mlv4-pass4.sc @@ -110,7 +110,7 @@ vec3 color_mix(vec3 c, vec3 color, vec3 E) ax.x = round(info); \ iq.x = dot(ax, bin) - 2.0; \ iq.y = dot(ay, bin) - 2.0; \ - PIXEL = texture2D(ORIG_texture, v_texcoord0 + iq.x * v_texcoord1.xy + iq.y * v_texcoord1.zw).xyz; \ + PIXEL = texture2D(ORIG_texture, v_texcoord0 + iq.x * v_texcoord1.xy + iq.y * v_texcoord1.zw).xyz; void main() diff --git a/src/osd/modules/render/bgfx/shaders/shader.mk b/src/osd/modules/render/bgfx/shaders/shader.mk index e63d171a3ca..cc656d5e828 100644 --- a/src/osd/modules/render/bgfx/shaders/shader.mk +++ b/src/osd/modules/render/bgfx/shaders/shader.mk @@ -1,5 +1,5 @@ # -# Copyright 2011-2021 Branimir Karadzic. All rights reserved. +# Copyright 2011-2021 Branimir Karadzic. # License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause # diff --git a/src/osd/modules/render/d3d/d3dhlsl.cpp b/src/osd/modules/render/d3d/d3dhlsl.cpp index 52bec85f06f..c0753f314f7 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.cpp +++ b/src/osd/modules/render/d3d/d3dhlsl.cpp @@ -2022,7 +2022,7 @@ int32_t slider::update(std::string *str, int32_t newval) return 0; } -char shaders::last_system_name[16]; +char shaders::last_system_name[MAX_DRIVER_NAME_CHARS + 1]; hlsl_options shaders::last_options = { false }; diff --git a/src/osd/modules/render/d3d/d3dhlsl.h b/src/osd/modules/render/d3d/d3dhlsl.h index f06deca2a57..6820f3dd316 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.h +++ b/src/osd/modules/render/d3d/d3dhlsl.h @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles //============================================================ // -// drawd3d.c - Win32 Direct3D HLSL-specific header +// drawd3d.h - Win32 Direct3D HLSL-specific header // //============================================================ @@ -457,7 +457,7 @@ private: static slider_desc s_sliders[]; static hlsl_options last_options; // last used options - static char last_system_name[16]; // last used system + static char last_system_name[MAX_DRIVER_NAME_CHARS + 1]; // last used system osd::dynamic_module::ptr d3dx9_dll; d3dx_create_effect_from_file_fn d3dx_create_effect_from_file_ptr; diff --git a/src/osd/modules/render/drawbgfx.cpp b/src/osd/modules/render/drawbgfx.cpp index 36758914961..ef90478e59e 100644 --- a/src/osd/modules/render/drawbgfx.cpp +++ b/src/osd/modules/render/drawbgfx.cpp @@ -56,10 +56,6 @@ extern void *GetOSWindow(void *wincontroller); #endif #endif -#if defined(SDLMAME_USE_WAYLAND) -#include <wayland-egl.h> -#endif - #include <bgfx/bgfx.h> #include <bgfx/platform.h> @@ -387,36 +383,6 @@ bool video_bgfx::init_bgfx_library(osd_window &window) //============================================================ -// Helper for creating a wayland window -//============================================================ - -#if defined(SDLMAME_USE_WAYLAND) -wl_egl_window *create_wl_egl_window(SDL_Window *window, struct wl_surface *surface) -{ - if (!surface) - { - osd_printf_error("Wayland surface missing, aborting\n"); - return nullptr; - } - wl_egl_window *win_impl = (wl_egl_window *)SDL_GetWindowData(window, "wl_egl_window"); - if (!win_impl) - { - int width, height; - SDL_GetWindowSize(window, &width, &height); - win_impl = wl_egl_window_create(surface, width, height); - if (!win_impl) - { - osd_printf_error("Creating wayland window failed\n"); - return nullptr; - } - SDL_SetWindowData(window, "wl_egl_window", win_impl); - } - return win_impl; -} -#endif - - -//============================================================ // Utility for setting up window handle //============================================================ @@ -428,6 +394,9 @@ bool video_bgfx::set_platform_data(bgfx::PlatformData &platform_data, osd_window #elif defined(OSD_MAC) platform_data.ndt = nullptr; platform_data.nwh = GetOSWindow(dynamic_cast<mac_window_info const &>(window).platform_window()); +#elif defined(SDLMAME_EMSCRIPTEN) + platform_data.ndt = nullptr; + platform_data.nwh = (void *)"#canvas"; // HTML5 target selector #else // defined(OSD_*) SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); @@ -457,10 +426,10 @@ bool video_bgfx::set_platform_data(bgfx::PlatformData &platform_data, osd_window platform_data.nwh = wmi.info.cocoa.window; break; #endif -#if defined(SDL_VIDEO_DRIVER_WAYLAND) && SDL_VERSION_ATLEAST(2, 0, 16) && defined(SDLMAME_USE_WAYLAND) +#if defined(SDL_VIDEO_DRIVER_WAYLAND) && SDL_VERSION_ATLEAST(2, 0, 16) case SDL_SYSWM_WAYLAND: platform_data.ndt = wmi.info.wl.display; - platform_data.nwh = create_wl_egl_window(dynamic_cast<sdl_window_info const &>(window).platform_window(), wmi.info.wl.surface); + platform_data.nwh = wmi.info.wl.surface; if (!platform_data.nwh) { osd_printf_error("BGFX: Error creating a Wayland window\n"); @@ -553,12 +522,9 @@ static std::pair<void *, bool> sdlNativeWindowHandle(SDL_Window *window) case SDL_SYSWM_COCOA: return std::make_pair(wmi.info.cocoa.window, true); #endif -#if defined(SDL_VIDEO_DRIVER_WAYLAND) && SDL_VERSION_ATLEAST(2, 0, 16) && defined(SDLMAME_USE_WAYLAND) +#if defined(SDL_VIDEO_DRIVER_WAYLAND) && SDL_VERSION_ATLEAST(2, 0, 16) case SDL_SYSWM_WAYLAND: - { - void *const platform_window = osd::create_wl_egl_window(window, wmi.info.wl.surface); - return std::make_pair(platform_window, platform_window != nullptr); - } + return std::make_pair(wmi.info.wl.surface, true); #endif #if defined(SDL_VIDEO_DRIVER_ANDROID) case SDL_SYSWM_ANDROID: diff --git a/src/osd/modules/render/drawogl.cpp b/src/osd/modules/render/drawogl.cpp index bc7fcf9a959..f9f76946bf4 100644 --- a/src/osd/modules/render/drawogl.cpp +++ b/src/osd/modules/render/drawogl.cpp @@ -316,8 +316,8 @@ public: #endif private: - static const uint32_t HASH_SIZE = ((1 << 10) + 1); - static const uint32_t OVERFLOW_SIZE = (1 << 10); + static const uint32_t HASH_SIZE = ((1 << 18) + 1); + static const uint32_t OVERFLOW_SIZE = (1 << 12); void destroy_all_textures(); diff --git a/src/osd/modules/render/drawsdl.cpp b/src/osd/modules/render/drawsdl.cpp index f806815b2b0..b1a9e93f42e 100644 --- a/src/osd/modules/render/drawsdl.cpp +++ b/src/osd/modules/render/drawsdl.cpp @@ -126,13 +126,13 @@ private: // YUV overlays -static void yuv_RGB_to_YV12(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YV12(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height); -static void yuv_RGB_to_YV12X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YV12X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height); -static void yuv_RGB_to_YUY2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YUY2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height); -static void yuv_RGB_to_YUY2X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YUY2X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height); //============================================================ @@ -552,7 +552,7 @@ void renderer_sdl1::yuv_init() //uint32_t *lookup = sdl->m_yuv_lookup; -static void yuv_RGB_to_YV12(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YV12(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height) { int x, y; @@ -606,7 +606,7 @@ static void yuv_RGB_to_YV12(const uint16_t *bitmap, uint8_t *ptr, const int pitc } } -static void yuv_RGB_to_YV12X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YV12X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height) { /* this one is used when scale==2 */ @@ -641,7 +641,7 @@ static void yuv_RGB_to_YV12X2(const uint16_t *bitmap, uint8_t *ptr, const int pi } } -static void yuv_RGB_to_YUY2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YUY2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height) { /* this one is used when scale==2 */ @@ -667,7 +667,7 @@ static void yuv_RGB_to_YUY2(const uint16_t *bitmap, uint8_t *ptr, const int pitc } } -static void yuv_RGB_to_YUY2X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, \ +static void yuv_RGB_to_YUY2X2(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height) { /* this one is used when scale==2 */ diff --git a/src/osd/modules/sound/coreaudio_sound.cpp b/src/osd/modules/sound/coreaudio_sound.cpp index 2cdb9291168..7e9814af117 100644 --- a/src/osd/modules/sound/coreaudio_sound.cpp +++ b/src/osd/modules/sound/coreaudio_sound.cpp @@ -46,7 +46,6 @@ public: m_playpos(0), m_writepos(0), m_in_underrun(false), - m_scale(128), m_overflows(0), m_underflows(0) { @@ -60,8 +59,7 @@ public: // sound_module - virtual void update_audio_stream(bool is_throttled, int16_t const *buffer, int samples_this_frame) override; - virtual void set_mastervolume(int attenuation) override; + virtual void stream_sink_update(uint32_t, int16_t const *buffer, int samples_this_frame) override; private: struct node_detail @@ -83,14 +81,6 @@ private: uint32_t buffer_avail() const { return ((m_writepos >= m_playpos) ? m_buffer_size : 0) + m_playpos - m_writepos; } uint32_t buffer_used() const { return ((m_playpos > m_writepos) ? m_buffer_size : 0) + m_writepos - m_playpos; } - void copy_scaled(void *dst, void const *src, uint32_t bytes) const - { - bytes /= sizeof(int16_t); - int16_t const *s = (int16_t const *)src; - for (int16_t *d = (int16_t *)dst; bytes > 0; bytes--, s++, d++) - *d = (*s * m_scale) >> 7; - } - bool create_graph(osd_options const &options); bool add_output(char const *name); bool add_device_output(char const *name); @@ -178,7 +168,6 @@ private: uint32_t m_playpos; uint32_t m_writepos; bool m_in_underrun; - int32_t m_scale; unsigned m_overflows; unsigned m_underflows; }; @@ -240,7 +229,6 @@ int sound_coreaudio::init(osd_interface &osd, const osd_options &options) m_playpos = 0; m_writepos = m_headroom; m_in_underrun = false; - m_scale = 128; m_overflows = m_underflows = 0; // Initialise and start @@ -290,7 +278,7 @@ void sound_coreaudio::exit() } -void sound_coreaudio::update_audio_stream(bool is_throttled, int16_t const *buffer, int samples_this_frame) +void sound_coreaudio::stream_sink_update(uint32_t, int16_t const *buffer, int samples_this_frame) { if ((m_sample_rate == 0) || !m_buffer) return; @@ -318,13 +306,6 @@ void sound_coreaudio::update_audio_stream(bool is_throttled, int16_t const *buff } -void sound_coreaudio::set_mastervolume(int attenuation) -{ - int const clamped_attenuation = std::clamp(attenuation, -32, 0); - m_scale = (-32 == clamped_attenuation) ? 0 : (int32_t)(pow(10.0, clamped_attenuation / 20.0) * 128); -} - - bool sound_coreaudio::create_graph(osd_options const &options) { OSStatus err; @@ -902,43 +883,40 @@ CFPropertyListRef sound_coreaudio::load_property_list(char const *name) const (UInt8 const *)name, strlen(name), false); - if (nullptr == url) - { + if (!url) return nullptr; - } - CFDataRef data = nullptr; - SInt32 err; - Boolean const status = CFURLCreateDataAndPropertiesFromResource( - nullptr, - url, - &data, - nullptr, - nullptr, - &err); + CFReadStreamRef const stream = CFReadStreamCreateWithFile(nullptr, url); CFRelease(url); - if (!status) + if (!stream) { - osd_printf_error( - "Error reading data from %s (%ld)\n", - name, - (long)err); - if (nullptr != data) CFRelease(data); + osd_printf_error("Error opening file %s\n", name); + return nullptr; + } + if (!CFReadStreamOpen(stream)) + { + CFRelease(stream); + osd_printf_error("Error opening file %s\n", name); return nullptr; } CFErrorRef msg = nullptr; - CFPropertyListRef const result = CFPropertyListCreateWithData( + CFPropertyListRef const result = CFPropertyListCreateWithStream( nullptr, - data, + stream, + 0, kCFPropertyListImmutable, nullptr, &msg); - CFRelease(data); - if ((nullptr == result) || (nullptr != msg)) - { - std::unique_ptr<char []> const buf = (nullptr != msg) ? convert_cfstring_to_utf8(CFErrorCopyDescription(msg)) : nullptr; - if (nullptr != msg) + CFReadStreamClose(stream); + CFRelease(stream); + if (!result || msg) + { + CFStringRef const desc = msg ? CFErrorCopyDescription(msg) : nullptr; + std::unique_ptr<char []> const buf = desc ? convert_cfstring_to_utf8(desc) : nullptr; + if (desc) + CFRelease(desc); + if (msg) CFRelease(msg); if (buf) @@ -954,7 +932,8 @@ CFPropertyListRef sound_coreaudio::load_property_list(char const *name) const "Error creating property list from %s\n", name); } - if (nullptr != result) CFRelease(result); + if (result) + CFRelease(result); return nullptr; } @@ -986,7 +965,7 @@ OSStatus sound_coreaudio::render( } uint32_t const chunk = std::min(m_buffer_size - m_playpos, number_bytes); - copy_scaled((int8_t *)data->mBuffers[0].mData, &m_buffer[m_playpos], chunk); + memcpy((int8_t *)data->mBuffers[0].mData, &m_buffer[m_playpos], chunk); m_playpos += chunk; if (m_playpos >= m_buffer_size) m_playpos = 0; @@ -995,7 +974,7 @@ OSStatus sound_coreaudio::render( { assert(0U == m_playpos); assert(m_writepos >= (number_bytes - chunk)); - copy_scaled((int8_t *)data->mBuffers[0].mData + chunk, &m_buffer[0], number_bytes - chunk); + memcpy((int8_t *)data->mBuffers[0].mData + chunk, &m_buffer[0], number_bytes - chunk); m_playpos += number_bytes - chunk; } diff --git a/src/osd/modules/sound/direct_sound.cpp b/src/osd/modules/sound/direct_sound.cpp index 0e89bb1ac0d..7417def934c 100644 --- a/src/osd/modules/sound/direct_sound.cpp +++ b/src/osd/modules/sound/direct_sound.cpp @@ -120,13 +120,6 @@ public: assert(m_buffer); return m_buffer->Stop(); } - HRESULT set_volume(LONG volume) const - { - assert(m_buffer); - return m_buffer->SetVolume(volume); - } - HRESULT set_min_volume() { return set_volume(DSBVOLUME_MIN); } - HRESULT get_current_positions(DWORD &play_pos, DWORD &write_pos) const { assert(m_buffer); @@ -225,8 +218,7 @@ public: virtual void exit() override; // sound_module - virtual void update_audio_stream(bool is_throttled, int16_t const *buffer, int samples_this_frame) override; - virtual void set_mastervolume(int attenuation) override; + virtual void stream_sink_update(uint32_t, int16_t const *buffer, int samples_this_frame) override; private: HRESULT dsound_init(); @@ -297,11 +289,11 @@ void sound_direct_sound::exit() //============================================================ -// update_audio_stream +// stream_sink_update //============================================================ -void sound_direct_sound::update_audio_stream( - bool is_throttled, +void sound_direct_sound::stream_sink_update( + uint32_t, int16_t const *buffer, int samples_this_frame) { @@ -318,7 +310,7 @@ void sound_direct_sound::update_audio_stream( if (DS_OK != result) return; -//DWORD orig_write = write_position; + //DWORD orig_write = write_position; // normalize the write position so it is always after the play position if (write_position < play_position) write_position += m_stream_buffer.size(); @@ -334,7 +326,7 @@ void sound_direct_sound::update_audio_stream( // if we're between play and write positions, then bump forward, but only in full chunks while (stream_in < write_position) { -//printf("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)m_stream_buffer_in, (int)bytes_this_frame); + //printf("Underflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)m_stream_buffer_in, (int)bytes_this_frame); m_buffer_underflows++; stream_in += bytes_this_frame; } @@ -342,7 +334,7 @@ void sound_direct_sound::update_audio_stream( // if we're going to overlap the play position, just skip this chunk if ((stream_in + bytes_this_frame) > (play_position + m_stream_buffer.size())) { -//printf("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)m_stream_buffer_in, (int)bytes_this_frame); + //printf("Overflow: PP=%d WP=%d(%d) SI=%d(%d) BTF=%d\n", (int)play_position, (int)write_position, (int)orig_write, (int)stream_in, (int)m_stream_buffer_in, (int)bytes_this_frame); m_buffer_overflows++; return; } @@ -364,26 +356,6 @@ void sound_direct_sound::update_audio_stream( //============================================================ -// set_mastervolume -//============================================================ - -void sound_direct_sound::set_mastervolume(int attenuation) -{ - // clamp the attenuation to 0-32 range - attenuation = std::clamp(attenuation, -32, 0); - - // set the master volume - if (m_stream_buffer) - { - if (-32 == attenuation) - m_stream_buffer.set_min_volume(); - else - m_stream_buffer.set_volume(100 * attenuation); - } -} - - -//============================================================ // dsound_init //============================================================ diff --git a/src/osd/modules/sound/js_sound.cpp b/src/osd/modules/sound/js_sound.cpp index 7375d014141..1473ef9de2a 100644 --- a/src/osd/modules/sound/js_sound.cpp +++ b/src/osd/modules/sound/js_sound.cpp @@ -29,26 +29,16 @@ public: // sound_module - virtual void update_audio_stream(bool is_throttled, const int16_t *buffer, int samples_this_frame) + virtual void stream_sink_update(uint32_t, const int16_t *buffer, int samples_this_frame) { EM_ASM_ARGS( { // Forward audio stream update on to JS backend implementation. - jsmame_update_audio_stream($0, $1); + jsmame_steam_update($1, $2); }, (unsigned int)buffer, samples_this_frame); } - - virtual void set_mastervolume(int attenuation) - { - EM_ASM_ARGS( - { - // Forward volume update on to JS backend implementation. - jsmame_set_mastervolume($0); - }, - attenuation); - } }; #else // SDLMAME_EMSCRIPTEN diff --git a/src/osd/modules/sound/js_sound.js b/src/osd/modules/sound/js_sound.js index 571a5227f2b..25f19231ae9 100644 --- a/src/osd/modules/sound/js_sound.js +++ b/src/osd/modules/sound/js_sound.js @@ -98,30 +98,7 @@ function disconnect_old_event() { eventNode = null; }; -function set_mastervolume ( - // even though it's 'attenuation' the value is negative, so... - attenuation_in_decibels -) { - lazy_init(); - if (!context) return; - - // http://stackoverflow.com/questions/22604500/web-audio-api-working-with-decibels - // seemingly incorrect/broken. figures. welcome to Web Audio - // var gain_web_audio = 1.0 - Math.pow(10, 10 / attenuation_in_decibels); - - // HACK: Max attenuation in JSMESS appears to be 32. - // Hit ' then left/right arrow to test. - // FIXME: This is linear instead of log10 scale. - var gain_web_audio = 1.0 + (+attenuation_in_decibels / +32); - if (gain_web_audio < +0) - gain_web_audio = +0; - else if (gain_web_audio > +1) - gain_web_audio = +1; - - gain_node.gain.value = gain_web_audio; -}; - -function update_audio_stream ( +function stream_sink_update ( pBuffer, // pointer into emscripten heap. int16 samples samples_this_frame // int. number of samples at pBuffer address. ) { @@ -207,14 +184,12 @@ function sample_count() { } return { - set_mastervolume: set_mastervolume, - update_audio_stream: update_audio_stream, + stream_sink_update: stream_sink_update, get_context: get_context, sample_count: sample_count }; })(); -window.jsmame_set_mastervolume = jsmame_web_audio.set_mastervolume; -window.jsmame_update_audio_stream = jsmame_web_audio.update_audio_stream; +window.jsmame_stream_sink_update = jsmame_web_audio.stream_sink_update; window.jsmame_sample_count = jsmame_web_audio.sample_count; diff --git a/src/osd/modules/sound/none.cpp b/src/osd/modules/sound/none.cpp index ba1085f436b..bdb219636d0 100644 --- a/src/osd/modules/sound/none.cpp +++ b/src/osd/modules/sound/none.cpp @@ -27,11 +27,6 @@ public: virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual void exit() override { } - - // sound_module - - virtual void update_audio_stream(bool is_throttled, const int16_t *buffer, int samples_this_frame) override { } - virtual void set_mastervolume(int attenuation) override { } }; } // anonymous namespace diff --git a/src/osd/modules/sound/pa_sound.cpp b/src/osd/modules/sound/pa_sound.cpp index d5bb96a25af..68200a9c3f1 100644 --- a/src/osd/modules/sound/pa_sound.cpp +++ b/src/osd/modules/sound/pa_sound.cpp @@ -52,8 +52,7 @@ public: // sound_module - virtual void update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame) override; - virtual void set_mastervolume(int attenuation) override; + virtual void stream_sink_update(uint32_t, const s16 *buffer, int samples_this_frame) override; private: // Lock free SPSC ring buffer @@ -80,14 +79,14 @@ private: writepos.store((writepos + n) % size); } - int write(const T* src, int n, int attenuation) { + int write(const T* src, int n) { n = std::min<int>(n, size - reserve - count()); if (writepos + n > size) { - att_memcpy(buf + writepos, src, sizeof(T) * (size - writepos), attenuation); - att_memcpy(buf, src + (size - writepos), sizeof(T) * (n - (size - writepos)), attenuation); + memcpy(buf + writepos, src, sizeof(T) * (size - writepos)); + memcpy(buf, src + (size - writepos), sizeof(T) * (n - (size - writepos))); } else { - att_memcpy(buf + writepos, src, sizeof(T) * n, attenuation); + memcpy(buf + writepos, src, sizeof(T) * n); } increment_writepos(n); @@ -128,13 +127,6 @@ private: return n; } - - void att_memcpy(T* dest, const T* data, int n, int attenuation) { - int level = powf(10.0, attenuation / 20.0) * 32768; - n /= sizeof(T); - while (n--) - *dest++ = (*data++ * level) >> 15; - } }; enum @@ -159,7 +151,6 @@ private: int m_sample_rate; int m_audio_latency; - int m_attenuation; audio_buffer<s16>* m_ab; @@ -193,7 +184,6 @@ int sound_pa::init(osd_interface &osd, osd_options const &options) unsigned long frames_per_callback = paFramesPerBufferUnspecified; double callback_interval; - m_attenuation = options.volume(); m_underflows = 0; m_overflows = 0; m_has_overflowed = false; @@ -389,7 +379,7 @@ int sound_pa::callback(s16* output_buffer, size_t number_of_samples) m_ab->read(output_buffer, buf_ct); std::memset(output_buffer + buf_ct, 0, (number_of_samples - buf_ct) * sizeof(s16)); - // if update_audio_stream has been called, note the underflow + // if stream_sink_update has been called, note the underflow if (m_osd_ticks) m_has_underflowed = true; @@ -399,7 +389,7 @@ int sound_pa::callback(s16* output_buffer, size_t number_of_samples) return paContinue; } -void sound_pa::update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame) +void sound_pa::stream_sink_update(uint32_t, const s16 *buffer, int samples_this_frame) { if (!m_sample_rate) return; @@ -423,17 +413,12 @@ void sound_pa::update_audio_stream(bool is_throttled, const s16 *buffer, int sam m_has_overflowed = false; } - m_ab->write(buffer, samples_this_frame * 2, m_attenuation); + m_ab->write(buffer, samples_this_frame * 2); // for determining buffer overflows, take the sample here instead of in the callback m_osd_ticks = osd_ticks(); } -void sound_pa::set_mastervolume(int attenuation) -{ - m_attenuation = attenuation; -} - void sound_pa::exit() { if (!m_sample_rate) diff --git a/src/osd/modules/sound/pipewire_sound.cpp b/src/osd/modules/sound/pipewire_sound.cpp new file mode 100644 index 00000000000..8bf5b8e0afa --- /dev/null +++ b/src/osd/modules/sound/pipewire_sound.cpp @@ -0,0 +1,823 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + piepewire_sound.c + + PipeWire interface. + +***************************************************************************/ + +#include "sound_module.h" +#include "modules/osdmodule.h" + +#ifndef NO_USE_PIPEWIRE + +#define GNU_SOURCE +#include "modules/lib/osdobj_common.h" + +#include <pipewire/pipewire.h> +#include <pipewire/extensions/metadata.h> +#include <spa/debug/pod.h> +#include <spa/debug/dict.h> +#include <spa/pod/builder.h> +#include <spa/param/audio/raw-utils.h> +#include <rapidjson/document.h> + +#include <map> + +class sound_pipewire : public osd_module, public sound_module +{ +public: + sound_pipewire() + : osd_module(OSD_SOUND_PROVIDER, "pipewire"), sound_module() + { + } + virtual ~sound_pipewire() { } + + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override; + + virtual bool external_per_channel_volume() override { return true; } + virtual bool split_streams_per_source() override { return true; } + + virtual uint32_t get_generation() override; + virtual osd::audio_info get_information() override; + virtual uint32_t stream_sink_open(uint32_t node, std::string name, uint32_t rate) override; + virtual uint32_t stream_source_open(uint32_t node, std::string name, uint32_t rate) override; + virtual void stream_set_volumes(uint32_t id, const std::vector<float> &db) override; + virtual void stream_close(uint32_t id) override; + virtual void stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) override; + virtual void stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) override; + +private: + struct position_info { + uint32_t m_position; + std::array<double, 3> m_coords; + }; + + static const position_info position_infos[]; + + static const char *const typenames[]; + enum { AREC, APLAY }; + + struct node_info { + sound_pipewire *m_wire; + uint32_t m_id, m_osdid; + int m_type; + std::string m_serial; + std::string m_name; + std::string m_text_id; + + // Audio node info + uint32_t m_sinks, m_sources; + std::vector<uint32_t> m_position_codes; + std::vector<std::string> m_port_names; + std::vector<std::array<double, 3>> m_positions; + + osd::audio_rate_range m_rate; + bool m_has_s16; + bool m_has_iec958; + + pw_node *m_node; + spa_hook m_node_listener; + + node_info(sound_pipewire *wire, uint32_t id, uint32_t osdid, int type, std::string serial, std::string name, std::string text_id) : m_wire(wire), m_id(id), m_osdid(osdid), m_type(type), m_serial(serial), m_name(name), m_text_id(text_id), m_sinks(0), m_sources(0), m_rate{0, 0, 0}, m_has_s16(false), m_has_iec958(false), m_node(nullptr) { + spa_zero(m_node_listener); + } + }; + + struct stream_info { + sound_pipewire *m_wire; + bool m_is_output; + uint32_t m_osdid; + uint32_t m_node_id; + node_info *m_node; + uint32_t m_channels; + pw_stream *m_stream; + std::vector<float> m_volumes; + abuffer m_buffer; + + stream_info(sound_pipewire *wire, bool is_output, uint32_t osdid, uint32_t channels) : m_wire(wire), m_is_output(is_output), m_osdid(osdid), m_channels(channels), m_stream(nullptr), m_buffer(channels) {} + }; + + static const pw_core_events core_events; + static const pw_registry_events registry_events; + static const pw_node_events node_events; + static const pw_metadata_events default_events; + static const pw_stream_events stream_sink_events; + static const pw_stream_events stream_source_events; + + std::map<uint32_t, node_info> m_nodes; + std::map<uint32_t, uint32_t> m_node_osdid_to_id; + + std::map<uint32_t, stream_info> m_streams; + + pw_thread_loop *m_loop; + pw_context *m_context; + pw_core *m_core; + spa_hook m_core_listener; + pw_registry *m_registry; + spa_hook m_registry_listener; + pw_metadata *m_default; + spa_hook m_default_listener; + + std::string m_default_audio_sink; + std::string m_default_audio_source; + + uint32_t m_node_current_id, m_stream_current_id; + uint32_t m_generation; + bool m_wait_sync, m_wait_stream; + + void sync(); + + void core_event_done(uint32_t id, int seq); + static void s_core_event_done(void *data, uint32_t id, int seq); + + void register_node(uint32_t id, const spa_dict *props); + void register_port(uint32_t id, const spa_dict *props); + void register_link(uint32_t id, const spa_dict *props); + void register_default_metadata(uint32_t id); + void register_metadata(uint32_t id, const spa_dict *props); + void registry_event_global(uint32_t id, uint32_t permissions, const char *type, uint32_t version, const spa_dict *props); + static void s_registry_event_global(void *data, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const spa_dict *props); + + void registry_event_global_remove(uint32_t id); + static void s_registry_event_global_remove(void *data, uint32_t id); + + void node_event_param(node_info *node, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param); + static void s_node_event_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param); + + int default_event_property(uint32_t subject, const char *key, const char *type, const char *value); + static int s_default_event_property(void *data, uint32_t subject, const char *key, const char *type, const char *value); + + void stream_sink_event_process(stream_info *stream); + static void s_stream_sink_event_process(void *data); + + void stream_source_event_process(stream_info *stream); + static void s_stream_source_event_process(void *data); + + void stream_event_param_changed(stream_info *stream, uint32_t id, const spa_pod *param); + static void s_stream_event_param_changed(void *data, uint32_t id, const spa_pod *param); +}; + +// Try to more or less map to speaker.h positions + +const sound_pipewire::position_info sound_pipewire::position_infos[] = { + { SPA_AUDIO_CHANNEL_MONO, { 0.0, 0.0, 1.0 } }, + { SPA_AUDIO_CHANNEL_FL, { -0.2, 0.0, 1.0 } }, + { SPA_AUDIO_CHANNEL_FR, { 0.2, 0.0, 1.0 } }, + { SPA_AUDIO_CHANNEL_FC, { 0.0, 0.0, 1.0 } }, + { SPA_AUDIO_CHANNEL_LFE, { 0.0, -0.5, 1.0 } }, + { SPA_AUDIO_CHANNEL_RL, { -0.2, 0.0, -0.5 } }, + { SPA_AUDIO_CHANNEL_RR, { 0.2, 0.0, -0.5 } }, + { SPA_AUDIO_CHANNEL_RC, { 0.0, 0.0, -0.5 } }, + { SPA_AUDIO_CHANNEL_UNKNOWN, { 0.0, 0.0, 0.0 } } +}; + + +const char *const sound_pipewire::typenames[] = { + "Audio recorder", "Speaker" +}; + +const pw_core_events sound_pipewire::core_events = { + PW_VERSION_CORE_EVENTS, + nullptr, // info + s_core_event_done, + nullptr, // ping + nullptr, // error + nullptr, // remove_id + nullptr, // bound_id + nullptr, // add_mem + nullptr, // remove_mem + nullptr // bound_props +}; + +const pw_registry_events sound_pipewire::registry_events = { + PW_VERSION_REGISTRY_EVENTS, + s_registry_event_global, + s_registry_event_global_remove +}; + +const pw_node_events sound_pipewire::node_events = { + PW_VERSION_NODE_EVENTS, + nullptr, // info + s_node_event_param +}; + +const pw_metadata_events sound_pipewire::default_events = { + PW_VERSION_METADATA_EVENTS, + s_default_event_property +}; + +const pw_stream_events sound_pipewire::stream_sink_events = { + PW_VERSION_STREAM_EVENTS, + nullptr, // destroy + nullptr, // state changed + nullptr, // control info + nullptr, // io changed + s_stream_event_param_changed, + nullptr, // add buffer + nullptr, // remove buffer + s_stream_sink_event_process, + nullptr, // drained + nullptr, // command + nullptr // trigger done +}; + +const pw_stream_events sound_pipewire::stream_source_events = { + PW_VERSION_STREAM_EVENTS, + nullptr, // destroy + nullptr, // state changed + nullptr, // control info + nullptr, // io changed + s_stream_event_param_changed, + nullptr, // add buffer + nullptr, // remove buffer + s_stream_source_event_process, + nullptr, // drained + nullptr, // command + nullptr // trigger done +}; + +void sound_pipewire::register_node(uint32_t id, const spa_dict *props) +{ + const spa_dict_item *cls = spa_dict_lookup_item(props, PW_KEY_MEDIA_CLASS); + const spa_dict_item *desc = spa_dict_lookup_item(props, PW_KEY_NODE_DESCRIPTION); + const spa_dict_item *name = spa_dict_lookup_item(props, PW_KEY_NODE_NAME); + const spa_dict_item *serial = spa_dict_lookup_item(props, PW_KEY_OBJECT_SERIAL); + if(!cls) + return; + int type; + if(!strcmp(cls->value, "Audio/Source")) + type = AREC; + else if(!strcmp(cls->value, "Audio/Sink")) + type = APLAY; + else + return; + + m_node_osdid_to_id[m_node_current_id] = id; + auto &node = m_nodes.emplace(id, node_info(this, id, m_node_current_id++, type, serial->value, desc ? desc->value : "?", name ? name->value : "?")).first->second; + + // printf("node %03x: %s %s %s | %s\n", node.m_id, serial->value, typenames[node.m_type], node.m_name.c_str(), node.m_text_id.c_str()); + + node.m_node = (pw_node *)pw_registry_bind(m_registry, id, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, 0); + pw_node_add_listener(node.m_node, &node.m_node_listener, &node_events, &node); + pw_node_enum_params(node.m_node, 0, 3, 0, 0xffffffff, nullptr); + m_generation++; +} + +void sound_pipewire::register_port(uint32_t id, const spa_dict *props) +{ + uint32_t node = strtol(spa_dict_lookup_item(props, PW_KEY_NODE_ID)->value, nullptr, 10); + auto ind = m_nodes.find(node); + if(ind == m_nodes.end()) + return; + + const spa_dict_item *channel = spa_dict_lookup_item(props, PW_KEY_AUDIO_CHANNEL); + const spa_dict_item *dir = spa_dict_lookup_item(props, PW_KEY_PORT_DIRECTION); + bool is_in = dir && !strcmp(dir->value, "in") ; + uint32_t index = strtol(spa_dict_lookup_item(props, PW_KEY_PORT_ID)->value, nullptr, 10); + + if(is_in && ind->second.m_sinks <= index) + ind->second.m_sinks = index+1; + if(!is_in && ind->second.m_sources <= index) + ind->second.m_sources = index+1; + + auto &port_names = ind->second.m_port_names; + if(port_names.size() <= index) + port_names.resize(index+1); + + if(is_in || port_names[index].empty()) + port_names[index] = channel ? channel->value : "?"; + + m_generation++; + // printf("port %03x.%d %03x: %s\n", node, index, id, port_names[index].c_str()); +} + +void sound_pipewire::register_link(uint32_t id, const spa_dict *props) +{ + const spa_dict_item *input = spa_dict_lookup_item(props, PW_KEY_LINK_INPUT_NODE); + const spa_dict_item *output = spa_dict_lookup_item(props, PW_KEY_LINK_OUTPUT_NODE); + if(!input || !output) + return; + + uint32_t input_id = strtol(input->value, nullptr, 10); + uint32_t output_id = strtol(output->value, nullptr, 10); + + for(auto &si : m_streams) { + stream_info &stream = si.second; + if(stream.m_is_output && stream.m_node_id == output_id && (stream.m_node && stream.m_node->m_id != input_id)) { + auto ni = m_nodes.find(input_id); + if(ni != m_nodes.end()) { + stream.m_node = &ni->second; + m_generation ++; + return; + } + } + if(!stream.m_is_output && stream.m_node_id == input_id && (stream.m_node && stream.m_node->m_id != output_id)) { + auto ni = m_nodes.find(output_id); + if(ni != m_nodes.end()) { + stream.m_node = &ni->second; + m_generation ++; + return; + } + } + } +} + +void sound_pipewire::register_default_metadata(uint32_t id) +{ + m_default = (pw_metadata *)pw_registry_bind(m_registry, id, PW_TYPE_INTERFACE_Metadata, PW_VERSION_METADATA, 0); + pw_metadata_add_listener(m_default, &m_default_listener, &default_events, this); +} + +void sound_pipewire::register_metadata(uint32_t id, const spa_dict *props) +{ + const spa_dict_item *mn = spa_dict_lookup_item(props, PW_KEY_METADATA_NAME); + if(mn && !strcmp(mn->value, "default")) + register_default_metadata(id); +} + +void sound_pipewire::registry_event_global(uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const spa_dict *props) +{ + if(!strcmp(type, PW_TYPE_INTERFACE_Node)) + register_node(id, props); + else if(!strcmp(type, PW_TYPE_INTERFACE_Port)) + register_port(id, props); + else if(!strcmp(type, PW_TYPE_INTERFACE_Metadata)) + register_metadata(id, props); + else if(!strcmp(type, PW_TYPE_INTERFACE_Link)) + register_link(id, props); + else { + // printf("type %03x %s\n", id, type); + } +} + +void sound_pipewire::s_registry_event_global(void *data, uint32_t id, + uint32_t permissions, const char *type, uint32_t version, + const spa_dict *props) +{ + ((sound_pipewire *)data)->registry_event_global(id, permissions, type, version, props); +} + +void sound_pipewire::registry_event_global_remove(uint32_t id) +{ + auto ind = m_nodes.find(id); + if(ind == m_nodes.end()) + return; + + for(auto &istream : m_streams) + if(istream.second.m_node == &ind->second) + istream.second.m_node = nullptr; + m_nodes.erase(ind); + m_generation++; +} + +void sound_pipewire::s_registry_event_global_remove(void *data, uint32_t id) +{ + ((sound_pipewire *)data)->registry_event_global_remove(id); +} + +void sound_pipewire::node_event_param(node_info *node, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) +{ + if(id == SPA_PARAM_EnumFormat) { + const spa_pod_prop *subtype = spa_pod_find_prop(param, nullptr, SPA_FORMAT_mediaSubtype); + if(subtype) { + uint32_t sval; + if(!spa_pod_get_id(&subtype->value, &sval)) { + if(sval == SPA_MEDIA_SUBTYPE_raw) { + const spa_pod_prop *format = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_format); + const spa_pod_prop *rate = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate); + const spa_pod_prop *position = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position); + + if(format) { + uint32_t *entry; + SPA_POD_CHOICE_FOREACH((spa_pod_choice *)(&format->value), entry) { + if(*entry == SPA_AUDIO_FORMAT_S16) + node->m_has_s16 = true; + } + } + if(rate) { + if(rate->value.type == SPA_TYPE_Choice) { + struct spa_pod_choice_body *b = &((spa_pod_choice *)(&rate->value))->body; + uint32_t *choices = (uint32_t *)(b+1); + node->m_rate.m_default_rate = choices[0]; + if(b->type == SPA_CHOICE_Range) { + node->m_rate.m_min_rate = choices[1]; + node->m_rate.m_max_rate = choices[2]; + } else { + node->m_rate.m_min_rate = node->m_rate.m_default_rate; + node->m_rate.m_max_rate = node->m_rate.m_default_rate; + } + } + } + + if(position) { + uint32_t *entry; + node->m_position_codes.clear(); + node->m_positions.clear(); + SPA_POD_ARRAY_FOREACH((spa_pod_array *)(&position->value), entry) { + node->m_position_codes.push_back(*entry); + for(uint32_t i = 0;; i++) { + if((position_infos[i].m_position == *entry) || (position_infos[i].m_position == SPA_AUDIO_CHANNEL_UNKNOWN)) { + node->m_positions.push_back(position_infos[i].m_coords); + break; + } + } + } + } + } else if(sval == SPA_MEDIA_SUBTYPE_iec958) + node->m_has_iec958 = true; + } + } + m_generation++; + + } else + spa_debug_pod(2, nullptr, param); +} + +void sound_pipewire::s_node_event_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) +{ + node_info *n = (node_info *)data; + n->m_wire->node_event_param(n, seq, id, index, next, param); +} + +int sound_pipewire::default_event_property(uint32_t subject, const char *key, const char *type, const char *value) +{ + if(!value) + return 0; + std::string val = value; + if(!strcmp(type, "Spa:String:JSON")) { + rapidjson::Document json; + json.Parse(value); + if(json.IsObject() && json.HasMember("name") && json["name"].IsString()) + val = json["name"].GetString(); + } else + val = value; + + if(!strcmp(key, "default.audio.sink")) + m_default_audio_sink = val; + + else if(!strcmp(key, "default.audio.source")) + m_default_audio_source = val; + + return 0; +} + +int sound_pipewire::s_default_event_property(void *data, uint32_t subject, const char *key, const char *type, const char *value) +{ + return ((sound_pipewire *)data)->default_event_property(subject, key, type, value); +} + +int sound_pipewire::init(osd_interface &osd, osd_options const &options) +{ + spa_zero(m_core_listener); + spa_zero(m_registry_listener); + spa_zero(m_default_listener); + + m_node_current_id = 1; + m_stream_current_id = 1; + m_generation = 1; + + m_wait_sync = false; + m_wait_stream = false; + + pw_init(nullptr, nullptr); + m_loop = pw_thread_loop_new(nullptr, nullptr); + m_context = pw_context_new(pw_thread_loop_get_loop(m_loop), nullptr, 0); + m_core = pw_context_connect(m_context, nullptr, 0); + + if(!m_core) + return 1; + + pw_core_add_listener(m_core, &m_core_listener, &core_events, this); + + m_registry = pw_core_get_registry(m_core, PW_VERSION_REGISTRY, 0); + pw_registry_add_listener(m_registry, &m_registry_listener, ®istry_events, this); + + pw_thread_loop_start(m_loop); + + // The first sync ensures that the initial information request is + // completed, the second that the subsequent ones (parameters + // retrieval) are completed too. + sync(); + sync(); + + return 0; +} + +void sound_pipewire::core_event_done(uint32_t id, int seq) +{ + m_wait_sync = false; + pw_thread_loop_signal(m_loop, false); +} + +void sound_pipewire::s_core_event_done(void *data, uint32_t id, int seq) +{ + ((sound_pipewire *)data)->core_event_done(id, seq); +} + +void sound_pipewire::sync() +{ + pw_thread_loop_lock(m_loop); + m_wait_sync = true; + pw_core_sync(m_core, PW_ID_CORE, 0); + while(m_wait_sync) + pw_thread_loop_wait(m_loop); + pw_thread_loop_unlock(m_loop); +} + +void sound_pipewire::exit() +{ + pw_thread_loop_stop(m_loop); + for(const auto &si : m_streams) + pw_stream_destroy(si.second.m_stream); + for(const auto &ni : m_nodes) + pw_proxy_destroy((pw_proxy *)ni.second.m_node); + pw_proxy_destroy((pw_proxy *)m_default); + pw_proxy_destroy((pw_proxy *)m_registry); + pw_core_disconnect(m_core); + pw_context_destroy(m_context); + pw_thread_loop_destroy(m_loop); + pw_deinit(); +} + +uint32_t sound_pipewire::get_generation() +{ + pw_thread_loop_lock(m_loop); + uint32_t result = m_generation; + pw_thread_loop_unlock(m_loop); + return result; +} + +osd::audio_info sound_pipewire::get_information() +{ + osd::audio_info result; + pw_thread_loop_lock(m_loop); + result.m_nodes.resize(m_nodes.size()); + result.m_default_sink = 0; + result.m_default_source = 0; + result.m_generation = m_generation; + uint32_t node = 0; + for(auto &inode : m_nodes) { + result.m_nodes[node].m_name = inode.second.m_name; + result.m_nodes[node].m_id = inode.second.m_osdid; + result.m_nodes[node].m_rate = inode.second.m_rate; + result.m_nodes[node].m_sinks = inode.second.m_sinks; + result.m_nodes[node].m_sources = inode.second.m_sources; + result.m_nodes[node].m_port_names = inode.second.m_port_names; + result.m_nodes[node].m_port_positions = inode.second.m_positions; + + if(inode.second.m_text_id == m_default_audio_sink) + result.m_default_sink = inode.second.m_osdid; + if(inode.second.m_text_id == m_default_audio_source) + result.m_default_source = inode.second.m_osdid; + node ++; + } + + for(auto &istream : m_streams) + if(istream.second.m_node) + result.m_streams.emplace_back(osd::audio_info::stream_info { istream.second.m_osdid, istream.second.m_node->m_osdid, istream.second.m_volumes }); + + pw_thread_loop_unlock(m_loop); + return result; +} + +void sound_pipewire::stream_sink_event_process(stream_info *stream) +{ + pw_buffer *buffer = pw_stream_dequeue_buffer(stream->m_stream); + if(!buffer) + return; + + spa_buffer *sbuf = buffer->buffer; + stream->m_buffer.get((int16_t *)(sbuf->datas[0].data), buffer->requested); + + sbuf->datas[0].chunk->offset = 0; + sbuf->datas[0].chunk->stride = stream->m_channels * 2; + sbuf->datas[0].chunk->size = buffer->requested * stream->m_channels * 2; + + pw_stream_queue_buffer(stream->m_stream, buffer); +} + +void sound_pipewire::s_stream_sink_event_process(void *data) +{ + stream_info *info = (stream_info *)(data); + info->m_wire->stream_sink_event_process(info); +} + +void sound_pipewire::stream_source_event_process(stream_info *stream) +{ + pw_buffer *buffer = pw_stream_dequeue_buffer(stream->m_stream); + if(!buffer) + return; + + spa_buffer *sbuf = buffer->buffer; + stream->m_buffer.push((int16_t *)(sbuf->datas[0].data), sbuf->datas[0].chunk->size / stream->m_channels / 2); + pw_stream_queue_buffer(stream->m_stream, buffer); +} + +void sound_pipewire::s_stream_source_event_process(void *data) +{ + stream_info *info = (stream_info *)(data); + info->m_wire->stream_source_event_process(info); +} + +void sound_pipewire::stream_event_param_changed(stream_info *stream, uint32_t id, const spa_pod *param) +{ + if(id == SPA_PARAM_Props) { + const spa_pod_prop *vols = spa_pod_find_prop(param, nullptr, SPA_PROP_channelVolumes); + if(vols) { + bool initial = stream->m_volumes.empty(); + stream->m_volumes.clear(); + float *entry; + SPA_POD_ARRAY_FOREACH((spa_pod_array *)(&vols->value), entry) { + stream->m_volumes.push_back(osd::linear_to_db(*entry)); + } + if(!stream->m_volumes.empty()) { + if(initial) { + m_wait_stream = false; + pw_thread_loop_signal(m_loop, false); + } else + m_generation++; + } + } + } +} + +void sound_pipewire::s_stream_event_param_changed(void *data, uint32_t id, const spa_pod *param) +{ + stream_info *info = (stream_info *)(data); + info->m_wire->stream_event_param_changed(info, id, param); +} + +uint32_t sound_pipewire::stream_sink_open(uint32_t node, std::string name, uint32_t rate) +{ + pw_thread_loop_lock(m_loop); + auto ni = m_node_osdid_to_id.find(node); + if(ni == m_node_osdid_to_id.end()) { + pw_thread_loop_unlock(m_loop); + return 0; + } + node_info &snode = m_nodes.find(ni->second)->second; + + uint32_t id = m_stream_current_id++; + auto &stream = m_streams.emplace(id, stream_info(this, true, id, snode.m_sinks)).first->second; + + stream.m_stream = pw_stream_new_simple(pw_thread_loop_get_loop(m_loop), + name.c_str(), + pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", + PW_KEY_MEDIA_CATEGORY, "Playback", + PW_KEY_MEDIA_ROLE, "Game", + PW_KEY_TARGET_OBJECT, snode.m_serial.c_str(), + nullptr), + &stream_sink_events, + &stream); + stream.m_node = &snode; + + const spa_pod *params; + spa_audio_info_raw format = { + SPA_AUDIO_FORMAT_S16, + 0, + rate, + stream.m_channels + }; + for(uint32_t i=0; i != snode.m_sinks; i++) + format.position[i] = snode.m_position_codes[i]; + + uint8_t buffer[1024]; + spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + params = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &format); + + pw_stream_connect(stream.m_stream, + PW_DIRECTION_OUTPUT, + PW_ID_ANY, + pw_stream_flags(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS), + ¶ms, 1); + + m_wait_stream = true; + while(m_wait_stream) + pw_thread_loop_wait(m_loop); + + stream.m_node_id = pw_stream_get_node_id(stream.m_stream); + pw_thread_loop_unlock(m_loop); + + return id; +} + +uint32_t sound_pipewire::stream_source_open(uint32_t node, std::string name, uint32_t rate) +{ + pw_thread_loop_lock(m_loop); + auto ni = m_node_osdid_to_id.find(node); + if(ni == m_node_osdid_to_id.end()) { + pw_thread_loop_unlock(m_loop); + return 0; + } + node_info &snode = m_nodes.find(ni->second)->second; + + uint32_t id = m_stream_current_id++; + auto &stream = m_streams.emplace(id, stream_info(this, false, id, snode.m_sources)).first->second; + + stream.m_stream = pw_stream_new_simple(pw_thread_loop_get_loop(m_loop), + name.c_str(), + pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", + PW_KEY_MEDIA_CATEGORY, "Record", + PW_KEY_MEDIA_ROLE, "Game", + PW_KEY_TARGET_OBJECT, snode.m_serial.c_str(), + nullptr), + &stream_source_events, + &stream); + stream.m_node = &snode; + + const spa_pod *params; + spa_audio_info_raw format = { + SPA_AUDIO_FORMAT_S16, + 0, + rate, + stream.m_channels + }; + for(uint32_t i=0; i != snode.m_sources; i++) + format.position[i] = snode.m_position_codes[i]; + + uint8_t buffer[1024]; + spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + params = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &format); + + pw_stream_connect(stream.m_stream, + PW_DIRECTION_INPUT, + PW_ID_ANY, + pw_stream_flags(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS), + ¶ms, 1); + + m_wait_stream = true; + while(m_wait_stream) + pw_thread_loop_wait(m_loop); + + stream.m_node_id = pw_stream_get_node_id(stream.m_stream); + pw_thread_loop_unlock(m_loop); + + return id; +} + +void sound_pipewire::stream_set_volumes(uint32_t id, const std::vector<float> &db) +{ + pw_thread_loop_lock(m_loop); + auto si = m_streams.find(id); + if(si == m_streams.end()) { + pw_thread_loop_unlock(m_loop); + return; + } + stream_info &stream = si->second; + stream.m_volumes = db; + std::vector<float> linear; + for(float db1 : db) + linear.push_back(osd::db_to_linear(db1)); + pw_stream_set_control(stream.m_stream, SPA_PROP_channelVolumes, linear.size(), linear.data(), 0); + pw_thread_loop_unlock(m_loop); +} + +void sound_pipewire::stream_close(uint32_t id) +{ + pw_thread_loop_lock(m_loop); + auto si = m_streams.find(id); + if(si == m_streams.end()) { + pw_thread_loop_unlock(m_loop); + return; + } + stream_info &stream = si->second; + pw_stream_destroy(stream.m_stream); + m_streams.erase(si); + pw_thread_loop_unlock(m_loop); +} + +void sound_pipewire::stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) +{ + pw_thread_loop_lock(m_loop); + auto si = m_streams.find(id); + if(si == m_streams.end()) { + pw_thread_loop_unlock(m_loop); + return; + } + si->second.m_buffer.push(buffer, samples_this_frame); + pw_thread_loop_unlock(m_loop); +} + +void sound_pipewire::stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) +{ + pw_thread_loop_lock(m_loop); + auto si = m_streams.find(id); + if(si == m_streams.end()) { + pw_thread_loop_unlock(m_loop); + return; + } + si->second.m_buffer.get(buffer, samples_this_frame); + pw_thread_loop_unlock(m_loop); +} + +#else + MODULE_NOT_SUPPORTED(sound_pipewire, OSD_SOUND_PROVIDER, "pipewire") +#endif + +MODULE_DEFINITION(SOUND_PIPEWIRE, sound_pipewire) diff --git a/src/osd/modules/sound/pulse_sound.cpp b/src/osd/modules/sound/pulse_sound.cpp index a0b316bee84..820ca149c39 100644 --- a/src/osd/modules/sound/pulse_sound.cpp +++ b/src/osd/modules/sound/pulse_sound.cpp @@ -14,21 +14,12 @@ #ifndef NO_USE_PULSEAUDIO #define GNU_SOURCE -#include <fcntl.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <poll.h> - -#include <mutex> -#include <thread> + #include <pulse/pulseaudio.h> +#include <map> #include "modules/lib/osdobj_common.h" -using osd::s16; -using osd::u32; - class sound_pulse : public osd_module, public sound_module { public: @@ -40,52 +31,113 @@ public: virtual int init(osd_interface &osd, osd_options const &options) override; virtual void exit() override; - virtual void update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame) override; - virtual void set_mastervolume(int attenuation) override; + + virtual bool external_per_channel_volume() override { return false; } + virtual bool split_streams_per_source() override { return true; } + + virtual uint32_t get_generation() override; + virtual osd::audio_info get_information() override; + virtual uint32_t stream_sink_open(uint32_t node, std::string name, uint32_t rate) override; + virtual void stream_set_volumes(uint32_t id, const std::vector<float> &db) override; + virtual void stream_close(uint32_t id) override; + virtual void stream_sink_update(uint32_t, const int16_t *buffer, int samples_this_frame) override; private: - struct abuffer { - size_t cpos; - std::vector<u32> data; + struct position_info { + pa_channel_position_t m_position; + std::array<double, 3> m_coords; }; - std::thread *m_thread; - pa_mainloop *m_mainloop; - pa_context *m_context; - pa_stream *m_stream; - std::mutex m_mutex; + static const position_info position_infos[]; + static const char *const typenames[]; + enum { AREC, APLAY }; + + struct node_info { + sound_pulse *m_pulse; + uint32_t m_id, m_osdid; + int m_type; + std::string m_name, m_desc; + + // Audio node info + std::vector<pa_channel_position_t> m_position_codes; + std::vector<std::string> m_position_names; + std::vector<std::array<double, 3>> m_positions; + uint32_t m_sink_port_count, m_source_port_count; + + osd::audio_rate_range m_rate; + + node_info(sound_pulse *pulse, uint32_t id, uint32_t osdid, int type, std::string name, std::string desc) : m_pulse(pulse), m_id(id), m_osdid(osdid), m_type(type), m_name(name), m_desc(desc), m_sink_port_count(0), m_source_port_count(0), m_rate{0, 0, 0} { + } + }; + + struct stream_info { + sound_pulse *m_pulse; + uint32_t m_osdid; + uint32_t m_pulse_id; + node_info *m_target_node; + uint32_t m_channels; + pa_stream *m_stream; + std::vector<float> m_volumes; + abuffer m_buffer; + + stream_info(sound_pulse *pulse, uint32_t osdid, uint32_t channels) : m_pulse(pulse), m_osdid(osdid), m_pulse_id(0), m_channels(channels), m_stream(nullptr), m_volumes(channels), m_buffer(channels) {} + }; - std::vector<abuffer> m_buffers; + std::map<uint32_t, node_info> m_nodes; + std::map<uint32_t, uint32_t> m_node_osdid_to_id; - u32 m_last_sample; - int m_new_volume_value; - bool m_setting_volume; - bool m_new_volume; + std::map<uint32_t, stream_info> m_streams; + std::map<uint32_t, uint32_t> m_stream_pulse_id_to_osdid; - int m_pipe_to_sub[2]; - int m_pipe_to_main[2]; + pa_threaded_mainloop *m_mainloop; + pa_context *m_context; + uint32_t m_node_current_id, m_stream_current_id; + uint32_t m_generation; + bool m_wait_stream, m_wait_init; + + std::string m_default_audio_sink; + std::string m_default_audio_source; - static void i_volume_set_notify(pa_context *, int success, void *self); - void volume_set_notify(int success); + static void i_server_info(pa_context *, const pa_server_info *i, void *self); + void server_info(const pa_server_info *i); static void i_context_notify(pa_context *, void *self); void context_notify(); + static void i_context_subscribe(pa_context *, pa_subscription_event_type_t t, uint32_t idx, void *self); + void context_subscribe(pa_subscription_event_type_t t, uint32_t idx); static void i_stream_notify(pa_stream *, void *self); - void stream_notify(); + void stream_notify(stream_info *stream); static void i_stream_write_request(pa_stream *, size_t size, void *self); - void stream_write_request(size_t size); + void stream_write_request(stream_info *stream, size_t size); + static void i_source_info(pa_context *, const pa_source_info *i, int eol, void *self); + void source_info(const pa_source_info *i, int eol); + static void i_sink_info_new(pa_context *, const pa_sink_info *i, int eol, void *self); + void sink_info_new(const pa_sink_info *i, int eol); + static void i_sink_input_info_change(pa_context *, const pa_sink_input_info *i, int eol, void *self); + void sink_input_info_change(stream_info *stream, const pa_sink_input_info *i, int eol); - void make_pipes(); - - void mainloop_thread(); - void send_main(char c); - void send_sub(char c); - char get_main(); - char peek_main(); - void stop_mainloop(int err); void generic_error(const char *msg); void generic_pa_error(const char *msg, int err); }; +// Try to more or less map to speaker.h positions + +const sound_pulse::position_info sound_pulse::position_infos[] = { + { PA_CHANNEL_POSITION_MONO, { 0.0, 0.0, 1.0 } }, + { PA_CHANNEL_POSITION_FRONT_LEFT, { -0.2, 0.0, 1.0 } }, + { PA_CHANNEL_POSITION_FRONT_RIGHT, { 0.2, 0.0, 1.0 } }, + { PA_CHANNEL_POSITION_FRONT_CENTER, { 0.0, 0.0, 1.0 } }, + { PA_CHANNEL_POSITION_LFE, { 0.0, -0.5, 1.0 } }, + { PA_CHANNEL_POSITION_REAR_LEFT, { -0.2, 0.0, -0.5 } }, + { PA_CHANNEL_POSITION_REAR_RIGHT, { 0.2, 0.0, -0.5 } }, + { PA_CHANNEL_POSITION_REAR_CENTER, { 0.0, 0.0, -0.5 } }, + { PA_CHANNEL_POSITION_MAX, { 0.0, 0.0, 0.0 } } +}; + + +const char *const sound_pulse::typenames[] = { + "Audio recorder", "Speaker" +}; + void sound_pulse::generic_error(const char *msg) { perror(msg); @@ -98,205 +150,301 @@ void sound_pulse::generic_pa_error(const char *msg, int err) ::exit(1); } -char sound_pulse::peek_main() +void sound_pulse::context_notify() { - char c; - int err = read(m_pipe_to_main[0], &c, 1); - if(err != 1) { - if(err >= 0) { - fprintf(stderr, "peek_main: read returned %d, that's supposedly impossible\n", err); - ::exit(1); - } - if(errno == EAGAIN || errno == EWOULDBLOCK) { - // No data, no problem - return -1; - } - generic_error("peek_main: read"); + pa_context_state state = pa_context_get_state(m_context); + if(state == PA_CONTEXT_READY) { + pa_context_subscribe(m_context, PA_SUBSCRIPTION_MASK_ALL, nullptr, this); + pa_context_get_sink_info_list(m_context, i_sink_info_new, (void *)this); + + } else if(state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) { + m_generation = 0x80000000; + pa_threaded_mainloop_signal(m_mainloop, 0); } - return c; } -char sound_pulse::get_main() +void sound_pulse::i_context_notify(pa_context *, void *self) { - pollfd pfds[1]; - pfds[0].fd = m_pipe_to_main[0]; - pfds[0].events = POLL_IN; - pfds[0].revents = 0; - int err = poll(pfds, 1, -1); - if(err < 0) - generic_error("get_main: poll"); - - char c; - err = read(m_pipe_to_main[0], &c, 1); - if(err != 1) { - if(err >= 0) { - fprintf(stderr, "get_main: read returned %d, that's supposedly impossible\n", err); - ::exit(1); - } - generic_error("get_main: read"); - } - return c; + static_cast<sound_pulse *>(self)->context_notify(); } -void sound_pulse::send_main(char c) +void sound_pulse::stream_notify(stream_info *stream) { - int err = write(m_pipe_to_main[1], &c, 1); - if(err != 1) { - if(err >= 0) { - fprintf(stderr, "send_main: write returned %d, that's supposedly impossible\n", err); - ::exit(1); - } - if(errno == EAGAIN || errno == EWOULDBLOCK) { - fprintf(stderr, "send_main: write would block, pipe buffer overflowed, something is going very badly\n"); - ::exit(1); - } - generic_error("send_main: write"); + pa_stream_state state = pa_stream_get_state(stream->m_stream); + + if(state == PA_STREAM_READY || state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED) { + m_wait_stream = false; + pa_threaded_mainloop_signal(m_mainloop, 0); } } -void sound_pulse::send_sub(char c) +void sound_pulse::i_stream_notify(pa_stream *, void *self) { - int err = write(m_pipe_to_sub[1], &c, 1); - if(err != 1) { - if(err >= 0) { - fprintf(stderr, "send_sub: write returned %d, that's supposedly impossible\n", err); - ::exit(1); - } - if(errno == EAGAIN || errno == EWOULDBLOCK) { - fprintf(stderr, "send_sub: write would block, pipe buffer overflowed, something is going very badly\n"); - ::exit(1); - } - generic_error("send_sub: write"); - } + stream_info *si = static_cast<stream_info *>(self); + si->m_pulse->stream_notify(si); } -void sound_pulse::make_pipes() +void sound_pulse::stream_write_request(stream_info *stream, size_t size) { - if(pipe2(m_pipe_to_sub, O_NONBLOCK)) - generic_error("pipe2 pipe_to_sub"); - if(pipe2(m_pipe_to_main, O_NONBLOCK)) - generic_error("pipe2 pipe_to_main"); + // This is called with the thread locked + while(size) { + void *buffer; + size_t bsize = size; + int err = pa_stream_begin_write(stream->m_stream, &buffer, &bsize); + if(err) + generic_pa_error("stream begin write", err); + uint32_t frames = bsize/2/stream->m_channels; + uint32_t bytes = frames*2*stream->m_channels; + stream->m_buffer.get((int16_t *)buffer, frames); + err = pa_stream_write(stream->m_stream, buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE); + if(err) + generic_pa_error("stream write", err); + size -= bytes; + } } -void sound_pulse::context_notify() +void sound_pulse::i_stream_write_request(pa_stream *, size_t size, void *self) { - pa_context_state state = pa_context_get_state(m_context); - if(state == PA_CONTEXT_READY) - send_main('r'); + stream_info *si = static_cast<stream_info *>(self); + si->m_pulse->stream_write_request(si, size); +} - else if(state == PA_CONTEXT_FAILED) { - send_main('f'); - stop_mainloop(pa_context_errno(m_context)); - } else if(state == PA_CONTEXT_TERMINATED) { - send_main('t'); - stop_mainloop(0); +void sound_pulse::server_info(const pa_server_info *i) +{ + m_default_audio_sink = i->default_sink_name; + m_default_audio_source = i->default_source_name; + m_generation++; + if(m_wait_init) { + m_wait_init = false; + pa_threaded_mainloop_signal(m_mainloop, 0); } } -void sound_pulse::i_context_notify(pa_context *, void *self) +void sound_pulse::i_server_info(pa_context *, const pa_server_info *i, void *self) { - static_cast<sound_pulse *>(self)->context_notify(); + static_cast<sound_pulse *>(self)->server_info(i); } -void sound_pulse::stream_notify() +void sound_pulse::source_info(const pa_source_info *i, int eol) { - pa_stream_state state = pa_stream_get_state(m_stream); - - if(state == PA_STREAM_READY) - send_main('r'); - - else if(state == PA_STREAM_FAILED) { - send_main('f'); - stop_mainloop(pa_context_errno(m_context)); + if(eol) { + if(m_wait_init) + pa_context_get_server_info(m_context, i_server_info, (void *)this); + return; + } + auto ni = m_nodes.find(i->index); + if(ni != m_nodes.end()) { + // Add the monitoring sources to the node + ni->second.m_source_port_count = i->channel_map.channels; + return; + } - } else if(state == PA_STREAM_TERMINATED) - pa_context_disconnect(m_context); + m_node_osdid_to_id[m_node_current_id] = i->index; + auto &node = m_nodes.emplace(i->index, node_info(this, i->index, m_node_current_id++, AREC, i->name, i->description)).first->second; + + node.m_source_port_count = i->channel_map.channels; + for(int chan=0; chan != i->channel_map.channels; chan++) { + pa_channel_position_t pos = i->channel_map.map[chan]; + node.m_position_codes.push_back(pos); + node.m_position_names.push_back(pa_channel_position_to_pretty_string(pos)); + for(uint32_t j = 0;; j++) { + if((position_infos[j].m_position == pos) || (position_infos[j].m_position == PA_CHANNEL_POSITION_MAX)) { + node.m_positions.push_back(position_infos[j].m_coords); + break; + } + } + } } -void sound_pulse::i_stream_notify(pa_stream *, void *self) +void sound_pulse::i_source_info(pa_context *, const pa_source_info *i, int eol, void *self) { - static_cast<sound_pulse *>(self)->stream_notify(); + static_cast<sound_pulse *>(self)->source_info(i, eol); } -void sound_pulse::stream_write_request(size_t size) +void sound_pulse::sink_info_new(const pa_sink_info *i, int eol) { - if(size & 3) { - fprintf(stderr, "stream request with size %d not a multiple of 4.\n", int(size)); - ::exit(1); + if(eol) { + if(m_wait_init) + pa_context_get_source_info_list(m_context, i_source_info, (void *)this); + return; } - size >>= 2; - std::unique_lock<std::mutex> lock(m_mutex); - while(size) { - if(m_buffers.empty()) { - std::vector<u32> zero(size, m_last_sample); - int err = pa_stream_write(m_stream, zero.data(), size << 2, nullptr, 0, PA_SEEK_RELATIVE); - if(err) - generic_pa_error("stream write", err); - size = 0; - - } else { - auto &buf = m_buffers[0]; - size_t csz = size; - size_t cur = buf.data.size() - buf.cpos; - if(csz > cur) - csz = cur; - int err = pa_stream_write(m_stream, buf.data.data() + buf.cpos, csz << 2, nullptr, 0, PA_SEEK_RELATIVE); - if(err) - generic_pa_error("stream write", err); - if(csz == cur) - m_buffers.erase(m_buffers.begin()); - else - buf.cpos += csz; - size -= csz; + m_node_osdid_to_id[m_node_current_id] = i->index; + auto &node = m_nodes.emplace(i->index, node_info(this, i->index, m_node_current_id++, APLAY, i->name, i->description)).first->second; + + node.m_sink_port_count = i->channel_map.channels; + for(int chan=0; chan != i->channel_map.channels; chan++) { + pa_channel_position_t pos = i->channel_map.map[chan]; + node.m_position_codes.push_back(pos); + node.m_position_names.push_back(pa_channel_position_to_pretty_string(pos)); + for(uint32_t j = 0;; j++) { + if((position_infos[j].m_position == pos) || (position_infos[j].m_position == PA_CHANNEL_POSITION_MAX)) { + node.m_positions.push_back(position_infos[j].m_coords); + break; + } } } + m_generation++; } -void sound_pulse::i_stream_write_request(pa_stream *, size_t size, void *self) +void sound_pulse::i_sink_info_new(pa_context *, const pa_sink_info *i, int eol, void *self) { - static_cast<sound_pulse *>(self)->stream_write_request(size); + static_cast<sound_pulse *>(self)->sink_info_new(i, eol); } -void sound_pulse::mainloop_thread() +void sound_pulse::sink_input_info_change(stream_info *stream, const pa_sink_input_info *i, int eol) { - int err = 0; - pa_mainloop_run(m_mainloop, &err); - if(err) - generic_pa_error("mainloop stopped", err); + if(eol) + return; + + auto ni = m_nodes.find(i->sink); + if(ni != m_nodes.end()) + stream->m_target_node = &ni->second; + + for(uint32_t port = 0; port != stream->m_channels; port++) + stream->m_volumes[port] = pa_sw_volume_to_dB(i->volume.values[port]); + + m_generation++; } -void sound_pulse::stop_mainloop(int err) +void sound_pulse::i_sink_input_info_change(pa_context *, const pa_sink_input_info *i, int eol, void *self) { - pa_mainloop_quit(m_mainloop, err); + stream_info *stream = static_cast<stream_info *>(self); + stream->m_pulse->sink_input_info_change(stream, i, eol); } -int sound_pulse::init(osd_interface &osd, osd_options const &options) +void sound_pulse::context_subscribe(pa_subscription_event_type_t t, uint32_t idx) { - m_last_sample = 0; - m_setting_volume = false; - m_new_volume = false; - m_new_volume_value = 0; + // This is called with the thread locked + switch(int(t)) { + case PA_SUBSCRIPTION_EVENT_REMOVE | PA_SUBSCRIPTION_EVENT_SINK: + case PA_SUBSCRIPTION_EVENT_REMOVE | PA_SUBSCRIPTION_EVENT_SOURCE: { + auto si = m_nodes.find(idx); + if(si == m_nodes.end()) + break; + for(auto &istream : m_streams) + if(istream.second.m_target_node == &si->second) + istream.second.m_target_node = nullptr; + m_nodes.erase(si); + m_generation++; + break; + } + + case PA_SUBSCRIPTION_EVENT_NEW | PA_SUBSCRIPTION_EVENT_SINK: + pa_context_get_sink_info_by_index(m_context, idx, i_sink_info_new, this); + break; + + case PA_SUBSCRIPTION_EVENT_NEW | PA_SUBSCRIPTION_EVENT_SOURCE: + pa_context_get_source_info_by_index(m_context, idx, i_source_info, this); + break; + + case PA_SUBSCRIPTION_EVENT_CHANGE | PA_SUBSCRIPTION_EVENT_SERVER: + pa_context_get_server_info(m_context, i_server_info, (void *)this); + break; + + case PA_SUBSCRIPTION_EVENT_CHANGE | PA_SUBSCRIPTION_EVENT_SINK_INPUT: { + auto si1 = m_stream_pulse_id_to_osdid.find(idx); + if(si1 == m_stream_pulse_id_to_osdid.end()) + break; + auto si = m_streams.find(si1->second); + if(si == m_streams.end()) + break; + pa_context_get_sink_input_info(m_context, idx, i_sink_input_info_change, (void *)&si->second); + break; + } - m_mainloop = pa_mainloop_new(); - m_context = pa_context_new(pa_mainloop_get_api(m_mainloop), "MAME"); - pa_context_set_state_callback(m_context, i_context_notify, this); - int err = pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); + default: + break; + } +} - if(err) - generic_pa_error("pa_connect", err); +void sound_pulse::i_context_subscribe(pa_context *, pa_subscription_event_type_t t, uint32_t idx, void *self) +{ + static_cast<sound_pulse *>(self)->context_subscribe(t, idx); +} - make_pipes(); - m_thread = new std::thread(&sound_pulse::mainloop_thread, this); - char res = get_main(); - if(res != 'r') +int sound_pulse::init(osd_interface &osd, osd_options const &options) +{ + m_node_current_id = 1; + m_stream_current_id = 1; + m_generation = 0; + m_wait_stream = false; + m_wait_init = true; + + m_mainloop = pa_threaded_mainloop_new(); + m_context = pa_context_new(pa_threaded_mainloop_get_api(m_mainloop), "MAME"); + pa_context_set_state_callback(m_context, i_context_notify, this); + pa_context_set_subscribe_callback(m_context, i_context_subscribe, this); + pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); + pa_threaded_mainloop_start(m_mainloop); + + pa_threaded_mainloop_lock(m_mainloop); + while(m_wait_init) + pa_threaded_mainloop_wait(m_mainloop); + pa_threaded_mainloop_unlock(m_mainloop); + if(m_generation >= 0x80000000) return 1; - const int sample_rate = options.sample_rate(); + return 0; +} + +uint32_t sound_pulse::get_generation() +{ + pa_threaded_mainloop_lock(m_mainloop); + uint32_t result = m_generation; + pa_threaded_mainloop_unlock(m_mainloop); + return result; +} + +osd::audio_info sound_pulse::get_information() +{ + osd::audio_info result; + pa_threaded_mainloop_lock(m_mainloop); + result.m_nodes.resize(m_nodes.size()); + result.m_default_sink = 0; + result.m_default_source = 0; + result.m_generation = m_generation; + uint32_t node = 0; + for(auto &inode : m_nodes) { + result.m_nodes[node].m_name = inode.second.m_desc; + result.m_nodes[node].m_id = inode.second.m_osdid; + result.m_nodes[node].m_rate = inode.second.m_rate; + result.m_nodes[node].m_sinks = inode.second.m_sink_port_count; + result.m_nodes[node].m_sources = inode.second.m_source_port_count; + result.m_nodes[node].m_port_names = inode.second.m_position_names; + result.m_nodes[node].m_port_positions = inode.second.m_positions; + + if(inode.second.m_name == m_default_audio_sink) + result.m_default_sink = inode.second.m_osdid; + if(inode.second.m_name == m_default_audio_source) + result.m_default_source = inode.second.m_osdid; + node ++; + } + + for(auto &istream : m_streams) + if(istream.second.m_target_node) + result.m_streams.emplace_back(osd::audio_info::stream_info { istream.second.m_osdid, istream.second.m_target_node->m_osdid, istream.second.m_volumes }); + + pa_threaded_mainloop_unlock(m_mainloop); + return result; +} + +uint32_t sound_pulse::stream_sink_open(uint32_t node, std::string name, uint32_t rate) +{ + pa_threaded_mainloop_lock(m_mainloop); + auto ni = m_node_osdid_to_id.find(node); + if(ni == m_node_osdid_to_id.end()) { + pa_threaded_mainloop_unlock(m_mainloop); + return 0; + } + node_info &snode = m_nodes.find(ni->second)->second; + + uint32_t id = m_stream_current_id++; + auto &stream = m_streams.emplace(id, stream_info(this, id, snode.m_sink_port_count)).first->second; pa_sample_spec ss; #ifdef LSB_FIRST @@ -304,108 +452,78 @@ int sound_pulse::init(osd_interface &osd, osd_options const &options) #else ss.format = PA_SAMPLE_S16BE; #endif - ss.rate = sample_rate; - ss.channels = 2; - m_stream = pa_stream_new(m_context, "main output", &ss, nullptr); - pa_stream_set_state_callback(m_stream, i_stream_notify, this); - pa_stream_set_write_callback(m_stream, i_stream_write_request, this); + ss.rate = rate; + ss.channels = stream.m_channels; + stream.m_stream = pa_stream_new(m_context, name.c_str(), &ss, nullptr); + pa_stream_set_state_callback(stream.m_stream, i_stream_notify, &stream); + pa_stream_set_write_callback(stream.m_stream, i_stream_write_request, &stream); pa_buffer_attr battr; - battr.fragsize = sample_rate / 1000; - battr.maxlength = uint32_t(-1); - battr.minreq = sample_rate / 1000; + battr.fragsize = uint32_t(-1); + battr.maxlength = 1024; + battr.minreq = uint32_t(-1); battr.prebuf = uint32_t(-1); - battr.tlength = sample_rate / 1000; + battr.tlength = uint32_t(-1); - err = pa_stream_connect_playback(m_stream, nullptr, &battr, PA_STREAM_ADJUST_LATENCY, nullptr, nullptr); + int err = pa_stream_connect_playback(stream.m_stream, snode.m_name.c_str(), &battr, pa_stream_flags_t(PA_STREAM_ADJUST_LATENCY|PA_STREAM_START_UNMUTED), nullptr, nullptr); if(err) generic_pa_error("stream connect playback", err); - res = get_main(); - if(res != 'r') - return 1; - return 0; -} + stream.m_target_node = &snode; -void sound_pulse::update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame) -{ - std::unique_lock<std::mutex> lock(m_mutex); - m_buffers.resize(m_buffers.size() + 1); - auto &buf = m_buffers.back(); - buf.cpos = 0; - buf.data.resize(samples_this_frame); - memcpy(buf.data.data(), buffer, samples_this_frame*4); - m_last_sample = buf.data.back(); - - if(m_buffers.size() > 10) - // If there way too many buffers, drop some so only 10 are left (roughly 0.2s) - m_buffers.erase(m_buffers.begin(), m_buffers.begin() + m_buffers.size() - 10); - - else if(m_buffers.size() >= 5) - // If there are too many buffers, remove five sample per buffer - // to slowly resync to reduce latency (4 seconds to - // compensate one buffer roughly) - buf.cpos = 5; + m_wait_stream = true; + while(m_wait_stream) + pa_threaded_mainloop_wait(m_mainloop); + + stream.m_pulse_id = pa_stream_get_index(stream.m_stream); + m_stream_pulse_id_to_osdid[stream.m_pulse_id] = id; + + pa_threaded_mainloop_unlock(m_mainloop); + + return id; } -void sound_pulse::volume_set_notify(int success) +void sound_pulse::stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) { - std::unique_lock<std::mutex> lock(m_mutex); - if(m_new_volume) { - m_new_volume = false; - pa_cvolume vol; - pa_cvolume_set(&vol, 2, pa_sw_volume_from_dB(m_new_volume_value)); - pa_context_set_sink_input_volume(m_context, pa_stream_get_index(m_stream), &vol, i_volume_set_notify, this); - } else - m_setting_volume = false; + pa_threaded_mainloop_lock(m_mainloop); + auto si = m_streams.find(id); + if(si == m_streams.end()) { + pa_threaded_mainloop_unlock(m_mainloop); + return; + } + si->second.m_buffer.push(buffer, samples_this_frame); + pa_threaded_mainloop_unlock(m_mainloop); } -void sound_pulse::i_volume_set_notify(pa_context *, int success, void *self) +void sound_pulse::stream_set_volumes(uint32_t id, const std::vector<float> &db) { - static_cast<sound_pulse *>(self)->volume_set_notify(success); } -void sound_pulse::set_mastervolume(int attenuation) +void sound_pulse::stream_close(uint32_t id) { - if(!m_stream) + pa_threaded_mainloop_lock(m_mainloop); + auto si = m_streams.find(id); + if(si == m_streams.end()) { + pa_threaded_mainloop_unlock(m_mainloop); return; - - - std::unique_lock<std::mutex> lock(m_mutex); - if(m_setting_volume) { - m_new_volume = true; - m_new_volume_value = attenuation; - } else { - m_setting_volume = true; - pa_cvolume vol; - pa_cvolume_set(&vol, 2, pa_sw_volume_from_dB(attenuation)); - pa_context_set_sink_input_volume(m_context, pa_stream_get_index(m_stream), &vol, i_volume_set_notify, this); } + stream_info &stream = si->second; + pa_stream_set_state_callback(stream.m_stream, nullptr, &stream); + pa_stream_set_write_callback(stream.m_stream, nullptr, &stream); + pa_stream_disconnect(stream.m_stream); + m_streams.erase(si); + pa_threaded_mainloop_unlock(m_mainloop); } void sound_pulse::exit() { - if(!m_stream) - return; + for(const auto &si : m_streams) { + pa_stream_disconnect(si.second.m_stream); + pa_stream_unref(si.second.m_stream); + } - pa_stream_disconnect(m_stream); - while(get_main() != 't') {} - pa_stream_unref(m_stream); pa_context_unref(m_context); - m_thread->join(); - pa_mainloop_free(m_mainloop); - delete m_thread; - - close(m_pipe_to_sub[0]); - close(m_pipe_to_sub[1]); - close(m_pipe_to_main[0]); - close(m_pipe_to_main[1]); - - m_thread = nullptr; - m_mainloop = nullptr; - m_context = nullptr; - m_stream = nullptr; - m_buffers.clear(); + pa_threaded_mainloop_free(m_mainloop); } #else diff --git a/src/osd/modules/sound/sdl_sound.cpp b/src/osd/modules/sound/sdl_sound.cpp index ba7082312ea..35c11eae86d 100644 --- a/src/osd/modules/sound/sdl_sound.cpp +++ b/src/osd/modules/sound/sdl_sound.cpp @@ -21,452 +21,208 @@ #include <SDL2/SDL.h> #include <algorithm> +#include <cmath> #include <fstream> #include <memory> +#include <map> namespace osd { namespace { -//============================================================ -// DEBUGGING -//============================================================ - -#define LOG_SOUND 0 - -#define SDLMAME_SOUND_LOG "sound.log" - - -//============================================================ -// CLASS -//============================================================ - class sound_sdl : public osd_module, public sound_module { public: - - // number of samples per SDL callback - static inline constexpr int SDL_XFER_SAMPLES = 512; - sound_sdl() : - osd_module(OSD_SOUND_PROVIDER, "sdl"), sound_module(), - sample_rate(0), - sdl_xfer_samples(SDL_XFER_SAMPLES), - stream_in_initialized(0), - attenuation(0), - buf_locked(0), - stream_buffer(nullptr), - stream_buffer_size(0), - buffer_underflows(0), - buffer_overflows(0) + osd_module(OSD_SOUND_PROVIDER, "sdl"), sound_module() { } + virtual ~sound_sdl() { } virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; - // sound_module + virtual bool external_per_channel_volume() override { return false; } + virtual bool split_streams_per_source() override { return false; } - virtual void update_audio_stream(bool is_throttled, const int16_t *buffer, int samples_this_frame) override; - virtual void set_mastervolume(int attenuation) override; + virtual uint32_t get_generation() override; + virtual osd::audio_info get_information() override; + virtual uint32_t stream_sink_open(uint32_t node, std::string name, uint32_t rate) override; + virtual void stream_close(uint32_t id) override; + virtual void stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) override; private: - class ring_buffer - { - public: - ring_buffer(size_t size); - - size_t data_size() const { return (tail - head + buffer_size) % buffer_size; } - size_t free_size() const { return (head - tail - 1 + buffer_size) % buffer_size; } - int append(const void *data, size_t size); - int pop(void *data, size_t size); - - private: - std::unique_ptr<int8_t []> const buffer; - size_t const buffer_size; - int head = 0, tail = 0; + struct device_info { + std::string m_name; + int m_freq; + uint8_t m_channels; + device_info(const char *name, int freq, uint8_t channels) : m_name(name), m_freq(freq), m_channels(channels) {} }; - static void sdl_callback(void *userdata, Uint8 *stream, int len); - - void lock_buffer(); - void unlock_buffer(); - void attenuate(int16_t *data, int bytes); - void copy_sample_data(bool is_throttled, const int16_t *data, int bytes_to_copy); - int sdl_create_buffers(); - void sdl_destroy_buffers(); - - int sample_rate; - int sdl_xfer_samples; - int stream_in_initialized; - int attenuation; + struct stream_info { + uint32_t m_id; + SDL_AudioDeviceID m_sdl_id; + abuffer m_buffer; + stream_info(uint32_t id, uint8_t channels) : m_id(id), m_sdl_id(0), m_buffer(channels) {} + }; - int buf_locked; - std::unique_ptr<ring_buffer> stream_buffer; - uint32_t stream_buffer_size; + std::vector<device_info> m_devices; + uint32_t m_default_sink; + uint32_t m_stream_next_id; + std::map<uint32_t, std::unique_ptr<stream_info>> m_streams; - // diagnostics - int buffer_underflows; - int buffer_overflows; - std::unique_ptr<std::ofstream> sound_log; + static void sink_callback(void *userdata, Uint8 *stream, int len); }; - -//============================================================ -// PARAMETERS -//============================================================ - -// maximum audio latency -#define MAX_AUDIO_LATENCY 5 - -//============================================================ -// ring_buffer - constructor -//============================================================ - -sound_sdl::ring_buffer::ring_buffer(size_t size) - : buffer(std::make_unique<int8_t []>(size + 1)), buffer_size(size + 1) -{ - // A size+1 bytes buffer is allocated because it can never be full. - // Otherwise the case head == tail couldn't be distinguished between a - // full buffer and an empty buffer. - std::fill_n(buffer.get(), size + 1, 0); -} - //============================================================ -// ring_buffer::append +// sound_sdl::init //============================================================ -int sound_sdl::ring_buffer::append(const void *data, size_t size) +int sound_sdl::init(osd_interface &osd, const osd_options &options) { - if (free_size() < size) - return -1; + m_stream_next_id = 1; - int8_t const *const data8 = reinterpret_cast<int8_t const *>(data); - size_t sz = buffer_size - tail; - if (size <= sz) - sz = size; - else - std::copy_n(&data8[sz], size - sz, &buffer[0]); - - std::copy_n(data8, sz, &buffer[tail]); - tail = (tail + size) % buffer_size; - - return 0; -} - -//============================================================ -// ring_buffer::pop -//============================================================ - -int sound_sdl::ring_buffer::pop(void *data, size_t size) -{ - if (data_size() < size) + if(SDL_InitSubSystem(SDL_INIT_AUDIO)) { + osd_printf_error("Could not initialize SDL %s\n", SDL_GetError()); return -1; - - int8_t *const data8 = reinterpret_cast<int8_t *>(data); - size_t sz = buffer_size - head; - if (size <= sz) - sz = size; - else - { - std::copy_n(&buffer[0], size - sz, &data8[sz]); - std::fill_n(&buffer[0], size - sz, 0); } - std::copy_n(&buffer[head], sz, data8); - std::fill_n(&buffer[head], sz, 0); - head = (head + size) % buffer_size; - - return 0; -} - -//============================================================ -// sound_sdl - destructor -//============================================================ - -//============================================================ -// lock_buffer -//============================================================ -void sound_sdl::lock_buffer() -{ - if (!buf_locked) - SDL_LockAudio(); - buf_locked++; - - if (LOG_SOUND) - *sound_log << "locking\n"; -} - -//============================================================ -// unlock_buffer -//============================================================ -void sound_sdl::unlock_buffer() -{ - buf_locked--; - if (!buf_locked) - SDL_UnlockAudio(); - - if (LOG_SOUND) - *sound_log << "unlocking\n"; - -} - -//============================================================ -// Apply attenuation -//============================================================ - -void sound_sdl::attenuate(int16_t *data, int bytes_to_copy) -{ - int level = (int) (pow(10.0, (double) attenuation / 20.0) * 128.0); - int count = bytes_to_copy / sizeof(*data); - while (count > 0) - { - *data = (*data * level) >> 7; /* / 128 */ - data++; - count--; + osd_printf_verbose("Audio: Start initialization\n"); + char const *const audio_driver = SDL_GetCurrentAudioDriver(); + osd_printf_verbose("Audio: Driver is %s\n", audio_driver ? audio_driver : "not initialized"); + + // Capture is not implemented in SDL2, and the enumeration + // interface is different in SDL3 + int dev_count = SDL_GetNumAudioDevices(0); + for(int i=0; i != dev_count; i++) { + SDL_AudioSpec spec; + const char *name = SDL_GetAudioDeviceName(i, 0); + int err = SDL_GetAudioDeviceSpec(i, 0, &spec); + if(!err) + m_devices.emplace_back(name, spec.freq, spec.channels); } + char *def_name; + SDL_AudioSpec def_spec; + if(!SDL_GetDefaultAudioInfo(&def_name, &def_spec, 0)) { + uint32_t idx; + for(idx = 0; idx != m_devices.size() && m_devices[idx].m_name != def_name; idx++); + if(idx == m_devices.size()) + m_devices.emplace_back(def_name, def_spec.freq, def_spec.channels); + m_default_sink = idx+1; + SDL_free(def_name); + } else + m_default_sink = 0; + return 0; } -//============================================================ -// copy_sample_data -//============================================================ - -void sound_sdl::copy_sample_data(bool is_throttled, const int16_t *data, int bytes_to_copy) -{ - lock_buffer(); - int const err = stream_buffer->append(data, bytes_to_copy); - unlock_buffer(); - - if (LOG_SOUND && err) - *sound_log << "Late detection of overflow. This shouldn't happen.\n"; -} - - -//============================================================ -// update_audio_stream -//============================================================ - -void sound_sdl::update_audio_stream(bool is_throttled, const int16_t *buffer, int samples_this_frame) +void sound_sdl::exit() { - // if nothing to do, don't do it - if (sample_rate == 0 || !stream_buffer) - return; - - if (!stream_in_initialized) - { - // Fill in some zeros to prevent an initial buffer underflow - int8_t zero = 0; - size_t zsize = stream_buffer->free_size() / 2; - while (zsize--) - stream_buffer->append(&zero, 1); - - // start playing - SDL_PauseAudio(0); - stream_in_initialized = 1; - } - - size_t bytes_this_frame = samples_this_frame * sizeof(*buffer) * 2; - size_t free_size = stream_buffer->free_size(); - size_t data_size = stream_buffer->data_size(); - - if (stream_buffer->free_size() < bytes_this_frame) { - if (LOG_SOUND) - util::stream_format(*sound_log, "Overflow: DS=%u FS=%u BTF=%u\n", data_size, free_size, bytes_this_frame); - buffer_overflows++; - return; - } - - copy_sample_data(is_throttled, buffer, bytes_this_frame); - - size_t nfree_size = stream_buffer->free_size(); - size_t ndata_size = stream_buffer->data_size(); - if (LOG_SOUND) - util::stream_format(*sound_log, "Appended data: DS=%u(%u) FS=%u(%u) BTF=%u\n", data_size, ndata_size, free_size, nfree_size, bytes_this_frame); + SDL_QuitSubSystem(SDL_INIT_AUDIO); } - - -//============================================================ -// set_mastervolume -//============================================================ - -void sound_sdl::set_mastervolume(int _attenuation) +uint32_t sound_sdl::get_generation() { - // clamp the attenuation to 0-32 range - attenuation = std::clamp(_attenuation, -32, 0); - - if (stream_in_initialized) - { - if (attenuation == -32) - SDL_PauseAudio(1); - else - SDL_PauseAudio(0); - } + // sdl2 is not dynamic w.r.t devices + return 1; } -//============================================================ -// sdl_callback -//============================================================ -void sound_sdl::sdl_callback(void *userdata, Uint8 *stream, int len) +osd::audio_info sound_sdl::get_information() { - sound_sdl *thiz = reinterpret_cast<sound_sdl *>(userdata); - size_t const free_size = thiz->stream_buffer->free_size(); - size_t const data_size = thiz->stream_buffer->data_size(); - - if (data_size < len) - { - thiz->buffer_underflows++; - if (LOG_SOUND) - util::stream_format(*thiz->sound_log, "Underflow at sdl_callback: DS=%u FS=%u Len=%d\n", data_size, free_size, len); + enum { FL, FR, FC, LFE, BL, BR, BC, SL, SR }; + static const char *const posname[9] = { "FL", "FR", "FC", "LFE", "BL", "BR", "BC", "SL", "SR" }; + + static std::array<double, 3> pos3d[9] = { + { -0.2, 0.0, 1.0 }, + { 0.2, 0.0, 1.0 }, + { 0.0, 0.0, 1.0 }, + { 0.0, -0.5, 1.0 }, + { -0.2, 0.0, -0.5 }, + { 0.2, 0.0, -0.5 }, + { 0.0, 0.0, -0.5 }, + { -0.2, 0.0, 0.0 }, + { 0.2, 0.0, 0.0 }, + }; + + static const uint32_t positions[8][8] = { + { FC }, + { FL, FR }, + { FL, FR, LFE }, + { FL, FR, BL, BR }, + { FL, FR, LFE, BL, BR }, + { FL, FR, FC, LFE, BL, BR }, + { FL, FR, FC, LFE, BC, SL, SR }, + { FL, FR, FC, LFE, BL, BR, SL, SR } + }; - // Maybe read whatever is left in the stream_buffer anyway? - memset(stream, 0, len); - return; + osd::audio_info result; + result.m_nodes.resize(m_devices.size()); + result.m_default_sink = m_default_sink; + result.m_default_source = 0; + result.m_generation = 1; + for(uint32_t node = 0; node != m_devices.size(); node++) { + result.m_nodes[node].m_name = m_devices[node].m_name; + result.m_nodes[node].m_id = node + 1; + uint32_t freq = m_devices[node].m_freq; + result.m_nodes[node].m_rate = audio_rate_range{ freq, freq, freq }; + result.m_nodes[node].m_sinks = m_devices[node].m_channels; + for(uint32_t port = 0; port != m_devices[node].m_channels; port++) { + uint32_t pos = positions[m_devices[node].m_channels-1][port]; + result.m_nodes[node].m_port_names.push_back(posname[pos]); + result.m_nodes[node].m_port_positions.push_back(pos3d[pos]); + } } - - int err = thiz->stream_buffer->pop((void *)stream, len); - if (LOG_SOUND && err) - *thiz->sound_log << "Late detection of underflow. This shouldn't happen.\n"; - - thiz->attenuate((int16_t *)stream, len); - - if (LOG_SOUND) - util::stream_format(*thiz->sound_log, "callback: xfer DS=%u FS=%u Len=%d\n", data_size, free_size, len); + return result; } - -//============================================================ -// sound_sdl::init -//============================================================ - -int sound_sdl::init(osd_interface &osd, const osd_options &options) +uint32_t sound_sdl::stream_sink_open(uint32_t node, std::string name, uint32_t rate) { - int n_channels = 2; - int audio_latency; - SDL_AudioSpec aspec, obtained; - - if (LOG_SOUND) - sound_log = std::make_unique<std::ofstream>(SDLMAME_SOUND_LOG); - - // skip if sound disabled - sample_rate = options.sample_rate(); - if (sample_rate != 0) - { - if (SDL_InitSubSystem(SDL_INIT_AUDIO)) - { - osd_printf_error("Could not initialize SDL %s\n", SDL_GetError()); - return -1; - } - - osd_printf_verbose("Audio: Start initialization\n"); - char const *const audio_driver = SDL_GetCurrentAudioDriver(); - osd_printf_verbose("Audio: Driver is %s\n", audio_driver ? audio_driver : "not initialized"); - - sdl_xfer_samples = SDL_XFER_SAMPLES; - stream_in_initialized = 0; - - // set up the audio specs - aspec.freq = sample_rate; - aspec.format = AUDIO_S16SYS; // keep endian independent - aspec.channels = n_channels; - aspec.samples = sdl_xfer_samples; - aspec.callback = sdl_callback; - aspec.userdata = this; - - if (SDL_OpenAudio(&aspec, &obtained) < 0) - goto cant_start_audio; - - osd_printf_verbose("Audio: frequency: %d, channels: %d, samples: %d\n", - obtained.freq, obtained.channels, obtained.samples); - - sdl_xfer_samples = obtained.samples; - - // pin audio latency - audio_latency = std::clamp(options.audio_latency(), 1, MAX_AUDIO_LATENCY); - - // compute the buffer sizes - stream_buffer_size = (sample_rate * 2 * sizeof(int16_t) * (2 + audio_latency)) / 30; - stream_buffer_size = (stream_buffer_size / 1024) * 1024; - if (stream_buffer_size < 1024) - stream_buffer_size = 1024; - - // create the buffers - if (sdl_create_buffers()) - goto cant_create_buffers; - - // set the startup volume - set_mastervolume(attenuation); - osd_printf_verbose("Audio: End initialization\n"); + device_info &dev = m_devices[node-1]; + std::unique_ptr<stream_info> stream = std::make_unique<stream_info>(m_stream_next_id ++, dev.m_channels); + + SDL_AudioSpec dspec, ospec; + dspec.freq = rate; + dspec.format = AUDIO_S16SYS; + dspec.channels = dev.m_channels; + dspec.samples = 512; + dspec.callback = sink_callback; + dspec.userdata = stream.get(); + + stream->m_sdl_id = SDL_OpenAudioDevice(dev.m_name.c_str(), 0, &dspec, &ospec, 0); + if(!stream->m_sdl_id) return 0; - - // error handling - cant_create_buffers: - cant_start_audio: - osd_printf_verbose("Audio: Initialization failed. SDL error: %s\n", SDL_GetError()); - - return -1; - } - - return 0; + SDL_PauseAudioDevice(stream->m_sdl_id, 0); + uint32_t id = stream->m_id; + m_streams[stream->m_id] = std::move(stream); + return id; } - - -//============================================================ -// sdl_kill -//============================================================ - -void sound_sdl::exit() +void sound_sdl::stream_close(uint32_t id) { - // if nothing to do, don't do it - if (sample_rate == 0) + auto si = m_streams.find(id); + if(si == m_streams.end()) return; - - osd_printf_verbose("sdl_kill: closing audio\n"); - SDL_CloseAudio(); - - SDL_QuitSubSystem(SDL_INIT_AUDIO); - - // kill the buffers - sdl_destroy_buffers(); - - // print out over/underflow stats - if (buffer_overflows || buffer_underflows) - osd_printf_verbose("Sound buffer: overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows); - - if (LOG_SOUND) - { - util::stream_format(*sound_log, "Sound buffer: overflows=%d underflows=%d\n", buffer_overflows, buffer_underflows); - sound_log.reset(); - } + SDL_CloseAudioDevice(si->second->m_sdl_id); + m_streams.erase(si); } - - -//============================================================ -// dsound_create_buffers -//============================================================ - -int sound_sdl::sdl_create_buffers() +void sound_sdl::stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) { - osd_printf_verbose("sdl_create_buffers: creating stream buffer of %u bytes\n", stream_buffer_size); - - stream_buffer = std::make_unique<ring_buffer>(stream_buffer_size); - buf_locked = 0; - return 0; + auto si = m_streams.find(id); + if(si == m_streams.end()) + return; + stream_info *stream = si->second.get(); + SDL_LockAudioDevice(stream->m_sdl_id); + stream->m_buffer.push(buffer, samples_this_frame); + SDL_UnlockAudioDevice(stream->m_sdl_id); } -//============================================================ -// sdl_destroy_buffers -//============================================================ - -void sound_sdl::sdl_destroy_buffers() +void sound_sdl::sink_callback(void *userdata, uint8_t *data, int len) { - // release the buffer - stream_buffer.reset(); + stream_info *stream = reinterpret_cast<stream_info *>(userdata); + stream->m_buffer.get((int16_t *)data, len / 2 / stream->m_buffer.channels()); } } // anonymous namespace diff --git a/src/osd/modules/sound/sound_module.cpp b/src/osd/modules/sound/sound_module.cpp new file mode 100644 index 00000000000..5dc13eb3246 --- /dev/null +++ b/src/osd/modules/sound/sound_module.cpp @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders:O. Galibert + + +#include "emu.h" +#include "sound_module.h" + +void sound_module::abuffer::get(int16_t *data, uint32_t samples) +{ + uint32_t pos = 0; + while(pos != samples) { + if(m_buffers.empty()) { + while(pos != samples) { + memcpy(data, m_last_sample.data(), m_channels*2); + data += m_channels; + pos ++; + } + break; + } + + auto &buf = m_buffers.front(); + if(buf.m_data.empty()) { + m_buffers.erase(m_buffers.begin()); + continue; + } + + uint32_t avail = buf.m_data.size() / m_channels - buf.m_cpos; + if(avail > samples - pos) { + avail = samples - pos; + memcpy(data, buf.m_data.data() + buf.m_cpos * m_channels, avail * 2 * m_channels); + buf.m_cpos += avail; + break; + } + + memcpy(data, buf.m_data.data() + buf.m_cpos * m_channels, avail * 2 * m_channels); + m_buffers.erase(m_buffers.begin()); + pos += avail; + data += avail * m_channels; + } +} + +void sound_module::abuffer::push(const int16_t *data, uint32_t samples) +{ + m_buffers.resize(m_buffers.size() + 1); + auto &buf = m_buffers.back(); + buf.m_cpos = 0; + buf.m_data.resize(samples * m_channels); + memcpy(buf.m_data.data(), data, samples * 2 * m_channels); + memcpy(m_last_sample.data(), data + (samples-1) * m_channels, 2 * m_channels); + + if(m_buffers.size() > 10) + // If there are way too many buffers, drop some so only 10 are left (roughly 0.2s) + m_buffers.erase(m_buffers.begin(), m_buffers.begin() + m_buffers.size() - 10); + + else if(m_buffers.size() >= 5) + // If there are too many buffers, remove five samples per buffer + // to slowly resync to reduce latency (4 seconds to + // compensate one buffer, roughly) + buf.m_cpos = 5; +} + diff --git a/src/osd/modules/sound/sound_module.h b/src/osd/modules/sound/sound_module.h index 088da5ea8d0..2498db84107 100644 --- a/src/osd/modules/sound/sound_module.h +++ b/src/osd/modules/sound/sound_module.h @@ -9,11 +9,12 @@ #pragma once -#include <cstdint> +#include <osdepend.h> -//============================================================ -// CONSTANTS -//============================================================ +#include <cstdint> +#include <array> +#include <vector> +#include <string> #define OSD_SOUND_PROVIDER "sound" @@ -22,8 +23,57 @@ class sound_module public: virtual ~sound_module() = default; - virtual void update_audio_stream(bool is_throttled, const int16_t *buffer, int samples_this_frame) = 0; - virtual void set_mastervolume(int attenuation) = 0; + virtual uint32_t get_generation() { return 1; } + virtual osd::audio_info get_information() { + osd::audio_info result; + result.m_generation = 1; + result.m_default_sink = 1; + result.m_default_source = 0; + result.m_nodes.resize(1); + result.m_nodes[0].m_name = ""; + result.m_nodes[0].m_id = 1; + result.m_nodes[0].m_rate.m_default_rate = 0; // Magic value meaning "use configured sample rate" + result.m_nodes[0].m_rate.m_min_rate = 0; + result.m_nodes[0].m_rate.m_max_rate = 0; + result.m_nodes[0].m_sinks = 2; + result.m_nodes[0].m_sources = 0; + result.m_nodes[0].m_port_names.push_back("L"); + result.m_nodes[0].m_port_names.push_back("R"); + result.m_nodes[0].m_port_positions.emplace_back(std::array<double, 3>({ -0.2, 0.0, 1.0 })); + result.m_nodes[0].m_port_positions.emplace_back(std::array<double, 3>({ 0.2, 0.0, 1.0 })); + result.m_streams.resize(1); + result.m_streams[0].m_id = 1; + result.m_streams[0].m_node = 1; + return result; + } + virtual bool external_per_channel_volume() { return false; } + virtual bool split_streams_per_source() { return false; } + + virtual uint32_t stream_sink_open(uint32_t node, std::string name, uint32_t rate) { return 1; } + virtual uint32_t stream_source_open(uint32_t node, std::string name, uint32_t rate) { return 0; } + virtual void stream_set_volumes(uint32_t id, const std::vector<float> &db) {} + virtual void stream_close(uint32_t id) {} + virtual void stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) {} + virtual void stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) {} + +protected: + class abuffer { + public: + abuffer(uint32_t channels) : m_channels(channels), m_last_sample(channels, 0) {} + void get(int16_t *data, uint32_t samples); + void push(const int16_t *data, uint32_t samples); + uint32_t channels() const { return m_channels; } + + private: + struct buffer { + uint32_t m_cpos; + std::vector<int16_t> m_data; + }; + + uint32_t m_channels; + std::vector<int16_t> m_last_sample; + std::vector<buffer> m_buffers; + }; }; #endif // MAME_OSD_SOUND_SOUND_MODULE_H diff --git a/src/osd/modules/sound/xaudio2_sound.cpp b/src/osd/modules/sound/xaudio2_sound.cpp index d0243ba1267..a865f2527fe 100644 --- a/src/osd/modules/sound/xaudio2_sound.cpp +++ b/src/osd/modules/sound/xaudio2_sound.cpp @@ -214,8 +214,7 @@ public: void exit() override; // sound_module - void update_audio_stream(bool is_throttled, int16_t const *buffer, int samples_this_frame) override; - void set_mastervolume(int attenuation) override; + void stream_sink_update(uint32_t, int16_t const *buffer, int samples_this_frame) override; private: // Xaudio callbacks @@ -376,11 +375,11 @@ void sound_xaudio2::exit() } //============================================================ -// update_audio_stream +// stream_sink_update //============================================================ -void sound_xaudio2::update_audio_stream( - bool is_throttled, +void sound_xaudio2::stream_sink_update( + uint32_t, int16_t const *buffer, int samples_this_frame) { @@ -414,32 +413,6 @@ void sound_xaudio2::update_audio_stream( } //============================================================ -// set_mastervolume -//============================================================ - -void sound_xaudio2::set_mastervolume(int attenuation) -{ - if (!m_initialized) - return; - - assert(m_sourceVoice); - - HRESULT result; - - // clamp the attenuation to 0-32 range - attenuation = std::clamp(attenuation, -32, 0); - - // Ranges from 1.0 to XAUDIO2_MAX_VOLUME_LEVEL indicate additional gain - // Ranges from 0 to 1.0 indicate a reduced volume level - // 0 indicates silence - // We only support a reduction from 1.0, so we generate values in the range 0.0 to 1.0 - float scaledVolume = (32.0f + attenuation) / 32.0f; - - // set the master volume - HR_RETV(m_sourceVoice->SetVolume(scaledVolume)); -} - -//============================================================ // IXAudio2VoiceCallback::OnBufferEnd //============================================================ diff --git a/src/osd/osdcore.h b/src/osd/osdcore.h index f3f5fc2e9b3..b3dc2099913 100644 --- a/src/osd/osdcore.h +++ b/src/osd/osdcore.h @@ -19,6 +19,7 @@ #include <iosfwd> #include <string> #include <string_view> +#include <system_error> #include <utility> #include <vector> @@ -128,6 +129,8 @@ osd_ticks_t osd_ticks_per_second() noexcept; -----------------------------------------------------------------------------*/ void osd_sleep(osd_ticks_t duration) noexcept; + + /*************************************************************************** WORK ITEM INTERFACES ***************************************************************************/ @@ -350,6 +353,14 @@ void osd_work_item_release(osd_work_item *item); void osd_break_into_debugger(const char *message); +/// \brief Get cache line size in bytes +/// +/// This function gets the host CPU's level 1 cache line size in bytes. +/// \return A pair consisting of an error condition and the cache line +/// size in bytes if successful. +std::pair<std::error_condition, unsigned> osd_get_cache_line_size() noexcept; + + /*************************************************************************** UNCATEGORIZED INTERFACES diff --git a/src/osd/osdepend.h b/src/osd/osdepend.h index 8f1d86bc816..0c22440009f 100644 --- a/src/osd/osdepend.h +++ b/src/osd/osdepend.h @@ -16,16 +16,20 @@ #include "emufwd.h" #include "bitmap.h" +#include "interface/audio.h" +#include "interface/midiport.h" +#include "interface/nethandler.h" #include <cstdint> #include <memory> #include <string> +#include <string_view> #include <vector> - +#include <array> // forward references class input_type_entry; -class osd_midi_device; +namespace osd { class midi_input_port; class midi_output_port; } namespace ui { class menu_item; } @@ -62,7 +66,6 @@ public: class osd_interface { public: - // general overridables virtual void init(running_machine &machine) = 0; virtual void update(bool skip_redraw) = 0; @@ -75,9 +78,17 @@ public: virtual void wait_for_debugger(device_t &device, bool firststop) = 0; // audio overridables - virtual void update_audio_stream(const int16_t *buffer, int samples_this_frame) = 0; - virtual void set_mastervolume(int attenuation) = 0; virtual bool no_sound() = 0; + virtual bool sound_external_per_channel_volume() = 0; + virtual bool sound_split_streams_per_source() = 0; + virtual uint32_t sound_get_generation() = 0; + virtual osd::audio_info sound_get_information() = 0; + virtual uint32_t sound_stream_sink_open(uint32_t node, std::string name, uint32_t rate) = 0; + virtual uint32_t sound_stream_source_open(uint32_t node, std::string name, uint32_t rate) = 0; + virtual void sound_stream_close(uint32_t id) = 0; + virtual void sound_stream_sink_update(uint32_t id, const int16_t *buffer, int samples_this_frame) = 0; + virtual void sound_stream_source_update(uint32_t id, int16_t *buffer, int samples_this_frame) = 0; + virtual void sound_stream_set_volumes(uint32_t id, const std::vector<float> &db) = 0; // input overridables virtual void customize_input_type_list(std::vector<input_type_entry> &typelist) = 0; @@ -93,28 +104,17 @@ public: // command option overrides virtual bool execute_command(const char *command) = 0; - // midi interface - virtual std::unique_ptr<osd_midi_device> create_midi_device() = 0; + // MIDI interface + virtual std::unique_ptr<osd::midi_input_port> create_midi_input(std::string_view name) = 0; + virtual std::unique_ptr<osd::midi_output_port> create_midi_output(std::string_view name) = 0; + virtual std::vector<osd::midi_port_info> list_midi_ports() = 0; + + // network interface + virtual std::unique_ptr<osd::network_device> open_network_device(int id, osd::network_handler &handler) = 0; + virtual std::vector<osd::network_device_info> list_network_devices() = 0; protected: virtual ~osd_interface() { } }; - -/*************************************************************************** - MIDI I/O INTERFACES -***************************************************************************/ - -class osd_midi_device -{ -public: - virtual ~osd_midi_device() { } - virtual bool open_input(const char *devname) = 0; - virtual bool open_output(const char *devname) = 0; - virtual void close() = 0; - virtual bool poll() = 0; - virtual int read(uint8_t *pOut) = 0; - virtual void write(uint8_t data) = 0; -}; - #endif // MAME_OSD_OSDEPEND_H diff --git a/src/osd/osdnet.cpp b/src/osd/osdnet.cpp deleted file mode 100644 index aded09bcaff..00000000000 --- a/src/osd/osdnet.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Carl - -#include "emu.h" -#include "osdnet.h" - -#include "dinetwork.h" - -static class std::vector<std::unique_ptr<osd_netdev::entry_t>> netdev_list; - -void add_netdev(const char *name, const char *description, create_netdev func) -{ - auto entry = std::make_unique<osd_netdev::entry_t>(); - entry->id = netdev_list.size(); - strncpy(entry->name, name, 255); - entry->name[255] = '\0'; - strncpy(entry->description, (description != nullptr) ? description : "(no name)", 255); - entry->description[255] = '\0'; - entry->func = func; - netdev_list.push_back(std::move(entry)); -} - -void clear_netdev() -{ - netdev_list.clear(); -} - -const std::vector<std::unique_ptr<osd_netdev::entry_t>>& get_netdev_list() -{ - return netdev_list; -} - -class osd_netdev *open_netdev(int id, class device_network_interface *ifdev, int rate) -{ - for(auto &entry : netdev_list) - if(entry->id==id) - return entry->func(entry->name, ifdev, rate); - return nullptr; -} - -osd_netdev::osd_netdev(class device_network_interface *ifdev, int rate) -{ - m_dev = ifdev; - m_timer = ifdev->device().timer_alloc(FUNC(osd_netdev::recv), this); - m_timer->adjust(attotime::from_hz(rate), 0, attotime::from_hz(rate)); -} - -osd_netdev::~osd_netdev() -{ -} - -void osd_netdev::start() -{ - m_timer->enable(true); -} - -void osd_netdev::stop() -{ - m_timer->enable(false); -} - -int osd_netdev::send(uint8_t *buf, int len) -{ - return 0; -} - -void osd_netdev::recv(int32_t param) -{ - uint8_t *buf; - int len; - //const char atalkmac[] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff }; - while(m_timer->enabled() && (len = recv_dev(&buf))) - { -#if 0 - if(buf[0] & 1) - { - if(memcmp("\xff\xff\xff\xff\xff\xff", buf, 6) && memcmp(atalkmac, buf, 6) && !m_dev->mcast_chk(buf, len)) continue; - } - else { - //const unsigned char *ourmac = (const unsigned char *)get_mac(); - //printf("our mac: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X dst mac: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n", ourmac[0], ourmac[1], ourmac[2], ourmac[3], ourmac[4], ourmac[5], buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - if(memcmp(get_mac(), buf, 6) && !get_promisc()) continue; - } -#endif - - m_dev->recv_cb(buf, len); - } -} - -int osd_netdev::recv_dev(uint8_t **buf) -{ - return 0; -} - -void osd_netdev::set_mac(const char *mac) -{ -} - -void osd_netdev::set_promisc(bool promisc) -{ -} - -bool osd_netdev::get_promisc() -{ - if(m_dev) - return m_dev->get_promisc(); - return false; -} - -const char *osd_netdev::get_mac() -{ - if(m_dev) - return m_dev->get_mac(); - return "\0\0\0\0\0\0"; -} - -int netdev_count() -{ - return netdev_list.size(); -} - -void osd_list_network_adapters() -{ - #ifdef USE_NETWORK - int num_devs = netdev_list.size(); - - if (num_devs == 0) - { - printf("No network adapters were found\n"); - return; - } - - printf("Available network adapters:\n"); - for (auto &entry : netdev_list) - { - printf(" %s\n", entry->description); - } - - #else - printf("Network is not supported in this build\n"); - #endif -} diff --git a/src/osd/osdnet.h b/src/osd/osdnet.h deleted file mode 100644 index 05276e7e149..00000000000 --- a/src/osd/osdnet.h +++ /dev/null @@ -1,60 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Carl -#ifndef MAME_OSD_OSDNET_H -#define MAME_OSD_OSDNET_H - -#pragma once - -#include <algorithm> - -class osd_netdev; - -#define CREATE_NETDEV(name) class osd_netdev *name(const char *ifname, class device_network_interface *ifdev, int rate) -typedef class osd_netdev *(*create_netdev)(const char *ifname, class device_network_interface *ifdev, int rate); - -class osd_netdev -{ -public: - struct entry_t - { - entry_t() - { - std::fill(std::begin(name), std::end(name), 0); - std::fill(std::begin(description), std::end(description), 0); - } - - int id = 0; - char name[256]; - char description[256]; - create_netdev func = nullptr; - }; - osd_netdev(class device_network_interface *ifdev, int rate); - virtual ~osd_netdev(); - void start(); - void stop(); - - virtual int send(uint8_t *buf, int len); - virtual void set_mac(const char *mac); - virtual void set_promisc(bool promisc); - - const char *get_mac(); - bool get_promisc(); - -protected: - virtual int recv_dev(uint8_t **buf); - -private: - void recv(int32_t param); - - class device_network_interface *m_dev; - emu_timer *m_timer; -}; - -class osd_netdev *open_netdev(int id, class device_network_interface *ifdev, int rate); -void add_netdev(const char *name, const char *description, create_netdev func); -void clear_netdev(); -const std::vector<std::unique_ptr<osd_netdev::entry_t>>& get_netdev_list(); -int netdev_count(); -void osd_list_network_adapters(); - -#endif // MAME_OSD_OSDNET_H diff --git a/src/osd/sdl/osdsdl.cpp b/src/osd/sdl/osdsdl.cpp index 6c5aef9f08c..d9268869729 100644 --- a/src/osd/sdl/osdsdl.cpp +++ b/src/osd/sdl/osdsdl.cpp @@ -17,8 +17,8 @@ #include "ui/uimain.h" -#include <SDL2/SDL.h> - +#include <algorithm> +#include <cmath> #include <cstdio> #include <cstring> @@ -55,7 +55,6 @@ void defines_verbose() osd_printf_verbose("\n"); osd_printf_verbose("Build defines 1: "); MACRO_VERBOSE(LSB_FIRST); - MACRO_VERBOSE(PTR64); MACRO_VERBOSE(MAME_NOASM); MACRO_VERBOSE(MAME_DEBUG); MACRO_VERBOSE(BIGENDIAN); @@ -168,7 +167,9 @@ sdl_osd_interface::sdl_osd_interface(sdl_options &options) : m_modifier_keys(0), m_last_click_time(std::chrono::steady_clock::time_point::min()), m_last_click_x(0), - m_last_click_y(0) + m_last_click_y(0), + m_enable_touch(false), + m_next_ptrdev(0) { } @@ -260,6 +261,34 @@ void sdl_osd_interface::init(running_machine &machine) } } + /* do we want touch support or will we use mouse emulation? */ + m_enable_touch = options().enable_touch(); + try + { + if (m_enable_touch) + { + int const count(SDL_GetNumTouchDevices()); + m_ptrdev_map.reserve(std::max<int>(count + 1, 8)); + map_pointer_device(SDL_MOUSE_TOUCHID); + for (int i = 0; count > i; ++i) + { + SDL_TouchID const device(SDL_GetTouchDevice(i)); + if (device) + map_pointer_device(device); + } + } + else + { + m_ptrdev_map.reserve(1); + map_pointer_device(SDL_MOUSE_TOUCHID); + } + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + // survivable - it will still attempt to allocate mappings when it first sees devices + } + #if defined(SDLMAME_ANDROID) SDL_SetHint(SDL_HINT_VIDEO_EXTERNAL_CONTEXT, "1"); #endif @@ -343,60 +372,50 @@ void sdl_osd_interface::customize_input_type_list(std::vector<input_type_entry> entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_PGDN); break; - // OSD hotkeys use LCTRL and start at F3, they start at - // F3 because F1-F2 are hardcoded into many drivers to + // OSD hotkeys use LALT/LCTRL and start at F3, they start + // at F3 because F1-F2 are hardcoded into many drivers to // various dipswitches, and pressing them together with - // LCTRL will still press/toggle these dipswitches. - - // add a Not lcrtl condition to the reset key - case IPT_UI_SOFT_RESET: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F3, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LSHIFT); - break; - - // add a Not lcrtl condition to the show gfx key - case IPT_UI_SHOW_GFX: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F4, input_seq::not_code, KEYCODE_LCONTROL); - break; + // LALT/LCTRL will still press/toggle these dipswitches. - // LCTRL-F5 to toggle OpenGL filtering + // LALT-F10 to toggle OpenGL filtering case IPT_OSD_5: entry.configure_osd("TOGGLE_FILTER", N_p("input-name", "Toggle Filter")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, KEYCODE_LCONTROL); + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, KEYCODE_LALT); break; - // LCTRL-F6 to decrease OpenGL prescaling + // add a Not LALT condition to the throttle key + case IPT_UI_THROTTLE: + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, input_seq::not_code, KEYCODE_LALT); + break; + + // LALT-F8 to decrease OpenGL prescaling case IPT_OSD_6: entry.configure_osd("DECREASE_PRESCALE", N_p("input-name", "Decrease Prescaling")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F6, KEYCODE_LCONTROL); + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F8, KEYCODE_LALT); break; - // add a Not lcrtl condition to the toggle cheat key - case IPT_UI_TOGGLE_CHEAT: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F6, input_seq::not_code, KEYCODE_LCONTROL); + // add a Not LALT condition to the frameskip dec key + case IPT_UI_FRAMESKIP_DEC: + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F8, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_LSHIFT, input_seq::not_code, KEYCODE_RSHIFT); break; - // LCTRL-F7 to increase OpenGL prescaling + // LALT-F9 to increase OpenGL prescaling case IPT_OSD_7: entry.configure_osd("INCREASE_PRESCALE", N_p("input-name", "Increase Prescaling")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F7, KEYCODE_LCONTROL); + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F9, KEYCODE_LALT); + break; + + // add a Not LALT condition to the load state key + case IPT_UI_FRAMESKIP_INC: + entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F9, input_seq::not_code, KEYCODE_LALT); break; - // lshift-lalt-F12 for fullscreen video (BGFX) + // LSHIFT-LALT-F12 for fullscreen video (BGFX) case IPT_OSD_8: entry.configure_osd("RENDER_AVI", N_p("input-name", "Record Rendered Video")); entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LALT); break; - // add a Not lcrtl condition to the load state key - case IPT_UI_LOAD_STATE: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F7, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LSHIFT); - break; - - // add a Not lcrtl condition to the throttle key - case IPT_UI_THROTTLE: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, input_seq::not_code, KEYCODE_LCONTROL); - break; - // disable the config menu if the ALT key is down // (allows ALT-TAB to switch between apps) case IPT_UI_MENU: @@ -488,7 +507,9 @@ void sdl_osd_interface::process_events() } else if (m_modifier_keys & MODIFIER_KEY_SHIFT) { - if (event.key.keysym.sym == SDLK_6) // Ctrl-^ (RS) + if (event.key.keysym.sym == SDLK_2) // Ctrl-@ (NUL) + machine().ui_input().push_char_event(osd_common_t::window_list().front()->target(), 0x00); + else if (event.key.keysym.sym == SDLK_6) // Ctrl-^ (RS) machine().ui_input().push_char_event(osd_common_t::window_list().front()->target(), 0x1e); else if (event.key.keysym.sym == SDLK_MINUS) // Ctrl-_ (US) machine().ui_input().push_char_event(osd_common_t::window_list().front()->target(), 0x1f); @@ -512,74 +533,126 @@ void sdl_osd_interface::process_events() break; case SDL_MOUSEMOTION: + if (!m_enable_touch || (SDL_TOUCH_MOUSEID != event.motion.which)) { - int cx, cy; - auto const window = focus_window(event.motion); - if (window && window->xy_to_render_target(event.motion.x, event.motion.y, &cx, &cy)) - machine().ui_input().push_mouse_move_event(window->target(), cx, cy); + auto const window = window_from_id(event.motion.windowID); + if (!window) + break; + + unsigned device; + try + { + device = map_pointer_device(SDL_MOUSE_TOUCHID); + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + break; + } + + int x, y; + window->xy_to_render_target(event.motion.x, event.motion.y, &x, &y); + window->mouse_moved(device, x, y); } break; case SDL_MOUSEBUTTONDOWN: - //printf("But down %d %d %d %d %s\n", event.button.which, event.button.button, event.button.x, event.button.y, devinfo->name.c_str()); - if (event.button.button == 1) + case SDL_MOUSEBUTTONUP: + if (!m_enable_touch || (SDL_TOUCH_MOUSEID != event.button.which)) { - int cx, cy; - auto const window = focus_window(event.button); - if (window && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy)) + auto const window = window_from_id(event.button.windowID); + if (!window) + break; + + unsigned device; + try { - auto const double_click_speed = std::chrono::milliseconds(250); - auto const click = std::chrono::steady_clock::now(); - machine().ui_input().push_mouse_down_event(window->target(), cx, cy); - - // avoid overflow with std::chrono::time_point::min() by adding rather than subtracting - if (click < (m_last_click_time + double_click_speed) - && (cx >= (m_last_click_x - 4) && cx <= (m_last_click_x + 4)) - && (cy >= (m_last_click_y - 4) && cy <= (m_last_click_y + 4))) + device = map_pointer_device(SDL_MOUSE_TOUCHID); + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + break; + } + + int x, y; + window->xy_to_render_target(event.button.x, event.button.y, &x, &y); + unsigned button(event.button.button - 1); + if ((1 == button) || (2 == button)) + button ^= 3; + if (SDL_PRESSED == event.button.state) + window->mouse_down(device, x, y, button); + else + window->mouse_up(device, x, y, button); + } + break; + + case SDL_MOUSEWHEEL: + { + auto const window = window_from_id(event.wheel.windowID); + if (window) + { + unsigned device; + try { - m_last_click_time = std::chrono::time_point<std::chrono::steady_clock>::min(); - machine().ui_input().push_mouse_double_click_event(window->target(), cx, cy); + device = map_pointer_device(SDL_MOUSE_TOUCHID); } - else + catch (std::bad_alloc const &) { - m_last_click_time = click; - m_last_click_x = cx; - m_last_click_y = cy; + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + break; } +#if SDL_VERSION_ATLEAST(2, 0, 18) + window->mouse_wheel(device, std::lround(event.wheel.preciseY * 120)); +#else + window->mouse_wheel(device, event.wheel.y); +#endif } } - else if (event.button.button == 3) - { - int cx, cy; - auto const window = focus_window(event.button); - if (window != nullptr && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy)) - machine().ui_input().push_mouse_rdown_event(window->target(), cx, cy); - } break; - case SDL_MOUSEBUTTONUP: - //printf("But up %d %d %d %d\n", event.button.which, event.button.button, event.button.x, event.button.y); - if (event.button.button == 1) - { - int cx, cy; - auto const window = focus_window(event.button); - if (window && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy)) - machine().ui_input().push_mouse_up_event(window->target(), cx, cy); - } - else if (event.button.button == 3) + case SDL_FINGERMOTION: + case SDL_FINGERDOWN: + case SDL_FINGERUP: + if (m_enable_touch && (SDL_MOUSE_TOUCHID != event.tfinger.touchId)) { - int cx, cy; - auto window = focus_window(event.button); - if (window && window->xy_to_render_target(event.button.x, event.button.y, &cx, &cy)) - machine().ui_input().push_mouse_rup_event(window->target(), cx, cy); - } - break; + // ignore if it doesn't map to a window we own + auto const window = window_from_id(event.tfinger.windowID); + if (!window) + break; + + // map SDL touch device ID to a zero-based device number + unsigned device; + try + { + device = map_pointer_device(event.tfinger.touchId); + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + break; + } - case SDL_MOUSEWHEEL: - { - auto const window = focus_window(event.wheel); - if (window) - machine().ui_input().push_mouse_wheel_event(window->target(), 0, 0, event.wheel.y, 3); + // convert normalised coordinates to what MAME wants + auto const size = window->get_size(); + int const winx = std::lround(event.tfinger.x * size.width()); + int const winy = std::lround(event.tfinger.y * size.height()); + int x, y; + window->xy_to_render_target(winx, winy, &x, &y); + + // call appropriate window method + switch (event.type) + { + case SDL_FINGERMOTION: + window->finger_moved(event.tfinger.fingerId, device, x, y); + break; + case SDL_FINGERDOWN: + window->finger_down(event.tfinger.fingerId, device, x, y); + break; + case SDL_FINGERUP: + window->finger_up(event.tfinger.fingerId, device, x, y); + break; + } } break; } @@ -636,12 +709,37 @@ void sdl_osd_interface::process_window_event(SDL_Event const &event) break; case SDL_WINDOWEVENT_ENTER: - m_mouse_over_window = 1; + { + m_mouse_over_window = 1; + unsigned device; + try + { + device = map_pointer_device(SDL_MOUSE_TOUCHID); + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + break; + } + window->mouse_entered(device); + } break; case SDL_WINDOWEVENT_LEAVE: - machine().ui_input().push_mouse_leave_event(window->target()); - m_mouse_over_window = 0; + { + m_mouse_over_window = 0; + unsigned device; + try + { + device = map_pointer_device(SDL_MOUSE_TOUCHID); + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_osd_interface: error allocating pointer data\n"); + break; + } + window->mouse_left(device); + } break; case SDL_WINDOWEVENT_FOCUS_GAINED: @@ -705,7 +803,7 @@ void sdl_osd_interface::check_osd_inputs() if (USE_OPENGL) { - //FIXME: on a per window basis + // FIXME: on a per window basis if (machine().ui_input().pressed(IPT_OSD_5)) { video_config.filter = !video_config.filter; @@ -727,9 +825,28 @@ void sdl_osd_interface::check_osd_inputs() template <typename T> sdl_window_info *sdl_osd_interface::focus_window(T const &event) const { - // FIXME: SDL does not properly report the window for certain OS. - if (false) + // FIXME: SDL does not properly report the window for certain versions of Ubuntu - is this still relevant? + if (m_enable_touch) return window_from_id(event.windowID); else return m_focus_window; } + + +unsigned sdl_osd_interface::map_pointer_device(SDL_TouchID device) +{ + auto devpos(std::lower_bound( + m_ptrdev_map.begin(), + m_ptrdev_map.end(), + device, + [] (std::pair<SDL_TouchID, unsigned> const &mapping, SDL_TouchID id) + { + return mapping.first < id; + })); + if ((m_ptrdev_map.end() == devpos) || (device != devpos->first)) + { + devpos = m_ptrdev_map.emplace(devpos, device, m_next_ptrdev); + ++m_next_ptrdev; + } + return devpos->second; +} diff --git a/src/osd/sdl/osdsdl.h b/src/osd/sdl/osdsdl.h index c8ce167d699..74730f69df5 100644 --- a/src/osd/sdl/osdsdl.h +++ b/src/osd/sdl/osdsdl.h @@ -10,11 +10,14 @@ #include "modules/lib/osdobj_common.h" #include "modules/osdmodule.h" +#include <SDL2/SDL.h> + #include <cassert> #include <chrono> #include <memory> #include <mutex> #include <unordered_map> +#include <utility> #include <string> #include <vector> @@ -128,8 +131,6 @@ public: }; -union SDL_Event; - using sdl_event_manager = event_subscription_manager<SDL_Event, uint32_t>; @@ -167,10 +168,6 @@ public: virtual void process_events() override; -protected: - virtual void build_slider_list() override; - virtual void update_slider_list() override; - private: enum { @@ -194,6 +191,8 @@ private: bool mouse_over_window() const { return m_mouse_over_window > 0; } template <typename T> sdl_window_info *focus_window(T const &event) const; + unsigned map_pointer_device(SDL_TouchID device); + sdl_options &m_options; sdl_window_info *m_focus_window; int m_mouse_over_window; @@ -202,6 +201,10 @@ private: std::chrono::steady_clock::time_point m_last_click_time; int m_last_click_x; int m_last_click_y; + + bool m_enable_touch; + unsigned m_next_ptrdev; + std::vector<std::pair<SDL_TouchID, unsigned> > m_ptrdev_map; }; //============================================================ diff --git a/src/osd/sdl/sdlopts.cpp b/src/osd/sdl/sdlopts.cpp index 8e922cb7a4e..3953a47b7f8 100644 --- a/src/osd/sdl/sdlopts.cpp +++ b/src/osd/sdl/sdlopts.cpp @@ -66,8 +66,10 @@ const options_entry f_sdl_option_entries[] = { SDLOPTION_KEYMAP_FILE, "keymap.dat", core_options::option_type::PATH, "keymap filename" }, // joystick mapping - { nullptr, nullptr, core_options::option_type::HEADER, "SDL JOYSTICK MAPPING" }, + { nullptr, nullptr, core_options::option_type::HEADER, "SDL INPUT OPTIONS" }, + { SDLOPTION_ENABLE_TOUCH, "0", core_options::option_type::BOOLEAN, "enable touch input support" }, { SDLOPTION_SIXAXIS, "0", core_options::option_type::BOOLEAN, "use special handling for PS3 Sixaxis controllers" }, + { SDLOPTION_DUAL_LIGHTGUN ";dual", "0", core_options::option_type::BOOLEAN, "enable dual lightgun input" }, #if (USE_XINPUT) // lightgun mapping diff --git a/src/osd/sdl/sdlopts.h b/src/osd/sdl/sdlopts.h index d754a0e8619..f6338fe1db6 100644 --- a/src/osd/sdl/sdlopts.h +++ b/src/osd/sdl/sdlopts.h @@ -26,7 +26,9 @@ #define SDLOPTION_KEYMAP "keymap" #define SDLOPTION_KEYMAP_FILE "keymap_file" +#define SDLOPTION_ENABLE_TOUCH "enable_touch" #define SDLOPTION_SIXAXIS "sixaxis" +#define SDLOPTION_DUAL_LIGHTGUN "dual_lightgun" #if defined(USE_XINPUT) && USE_XINPUT #define SDLOPTION_LIGHTGUNINDEX "lightgun_index" #endif @@ -82,8 +84,10 @@ public: bool keymap() const { return bool_value(SDLOPTION_KEYMAP); } const char *keymap_file() const { return value(SDLOPTION_KEYMAP_FILE); } - // joystick mapping + // input options + bool enable_touch() const { return bool_value(SDLOPTION_ENABLE_TOUCH); } bool sixaxis() const { return bool_value(SDLOPTION_SIXAXIS); } + bool dual_lightgun() const { return bool_value(SDLOPTION_DUAL_LIGHTGUN); } const char *video_driver() const { return value(SDLOPTION_VIDEODRIVER); } const char *render_driver() const { return value(SDLOPTION_RENDERDRIVER); } diff --git a/src/osd/sdl/video.cpp b/src/osd/sdl/video.cpp index dddc4ef80e6..8fe836aabf2 100644 --- a/src/osd/sdl/video.cpp +++ b/src/osd/sdl/video.cpp @@ -62,7 +62,6 @@ bool sdl_osd_interface::video_init() for (index = 0; index < video_config.numscreens; index++) { osd_window_config conf; - memset(&conf, 0, sizeof(conf)); get_resolution(options().resolution(), options().resolution(index), &conf, true); // create window ... diff --git a/src/osd/sdl/window.cpp b/src/osd/sdl/window.cpp index 41f0c5af2be..59be82238d0 100644 --- a/src/osd/sdl/window.cpp +++ b/src/osd/sdl/window.cpp @@ -13,24 +13,27 @@ #include "emuopts.h" #include "render.h" #include "screen.h" +#include "uiinput.h" #include "ui/uimain.h" // OSD headers -#include "window.h" -#include "osdsdl.h" #include "modules/monitor/monitor_common.h" +#include "osdsdl.h" +#include "window.h" // standard SDL headers -#include <SDL2/SDL.h> #include <SDL2/SDL_syswm.h> // standard C headers +#include <algorithm> +#include <cassert> #include <cmath> +#include <list> +#include <memory> + #ifndef _MSC_VER #include <unistd.h> #endif -#include <list> -#include <memory> #ifdef SDLMAME_WIN32 #include <windows.h> @@ -61,11 +64,6 @@ #define SDL_VERSION_EQUALS(v1, vnum2) (SDL_VERSIONNUM(v1.major, v1.minor, v1.patch) == vnum2) -class SDL_DM_Wrapper -{ -public: - SDL_DisplayMode mode; -}; //============================================================ @@ -113,30 +111,6 @@ bool sdl_osd_interface::window_init() } -void sdl_osd_interface::update_slider_list() -{ - for (auto const &window : osd_common_t::window_list()) - { - // check if any window has dirty sliders - if (window->renderer().sliders_dirty()) - { - build_slider_list(); - return; - } - } -} - -void sdl_osd_interface::build_slider_list() -{ - m_sliders.clear(); - - for (auto const &window : osd_common_t::window_list()) - { - std::vector<ui::menu_item> window_sliders = window->renderer().get_slider_list(); - m_sliders.insert(m_sliders.end(), window_sliders.begin(), window_sliders.end()); - } -} - //============================================================ // sdlwindow_exit // (main thread) @@ -248,9 +222,9 @@ void sdl_window_info::toggle_full_screen() #endif if (fullscreen() && (video_config.switchres || is_osx)) { - SDL_SetWindowFullscreen(platform_window(), 0); // Try to set mode - SDL_SetWindowDisplayMode(platform_window(), &m_original_mode->mode); // Try to set mode - SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN); // Try to set mode + SDL_SetWindowFullscreen(platform_window(), 0); + SDL_SetWindowDisplayMode(platform_window(), &m_original_mode); + SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN); } SDL_DestroyWindow(platform_window()); set_platform_window(nullptr); @@ -266,7 +240,7 @@ void sdl_window_info::modify_prescale(int dir) { int new_prescale = prescale(); - if (dir > 0 && prescale() < 3) + if (dir > 0 && prescale() < 20) new_prescale = prescale() + 1; if (dir < 0 && prescale() > 1) new_prescale = prescale() - 1; @@ -286,8 +260,8 @@ void sdl_window_info::modify_prescale(int dir) m_prescale = new_prescale; notify_changed(); } - machine().ui().popup_time(1, "Prescale %d", prescale()); } + machine().ui().popup_time(1, "Prescale %d", prescale()); } //============================================================ @@ -334,6 +308,291 @@ int sdl_window_info::xy_to_render_target(int x, int y, int *xt, int *yt) return renderer().xy_to_render_target(x, y, xt, yt); } +void sdl_window_info::mouse_entered(unsigned device) +{ + m_mouse_inside = true; +} + +void sdl_window_info::mouse_left(unsigned device) +{ + m_mouse_inside = false; + + auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), SDL_FingerID(-1), &sdl_pointer_info::compare)); + if ((m_active_pointers.end() == info) || (info->finger != SDL_FingerID(-1))) + return; + + // leaving implicitly releases buttons, so check hold/drag if necessary + if (BIT(info->buttons, 0) && (0 < info->clickcnt)) + { + auto const now(std::chrono::steady_clock::now()); + auto const exp(std::chrono::milliseconds(250) + info->pressed); + int const dx(info->x - info->pressedx); + int const dy(info->y - info->pressedy); + int const distance((dx * dx) + (dy * dy)); + if ((exp < now) || (CLICK_DISTANCE < distance)) + info->clickcnt = -info->clickcnt; + } + + // push to UI manager + machine().ui_input().push_pointer_leave( + target(), + osd::ui_event_handler::pointer::MOUSE, + info->index, + device, + info->x, info->y, + info->buttons, info->clickcnt); + + // dump pointer data + m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index); + if (info->index < m_next_pointer) + m_next_pointer = info->index; + m_active_pointers.erase(info); +} + +void sdl_window_info::mouse_down(unsigned device, int x, int y, unsigned button) +{ + if (!m_mouse_inside) + return; + + auto const info(map_pointer(SDL_FingerID(-1), device)); + if (m_active_pointers.end() == info) + return; + + if ((x == info->x) && (y == info->y) && BIT(info->buttons, button)) + return; + + // detect multi-click actions + if (0 == button) + { + info->primary_down( + x, + y, + std::chrono::milliseconds(250), + CLICK_DISTANCE, + false, + m_ptrdev_info); + } + + // update info and push to UI manager + auto const pressed(decltype(info->buttons)(1) << button); + info->x = x; + info->y = y; + info->buttons |= pressed; + machine().ui_input().push_pointer_update( + target(), + osd::ui_event_handler::pointer::MOUSE, + info->index, + device, + x, y, + info->buttons, pressed, 0, info->clickcnt); +} + +void sdl_window_info::mouse_up(unsigned device, int x, int y, unsigned button) +{ + if (!m_mouse_inside) + return; + + auto const info(map_pointer(SDL_FingerID(-1), device)); + if (m_active_pointers.end() == info) + return; + + if ((x == info->x) && (y == info->y) && !BIT(info->buttons, button)) + return; + + // detect multi-click actions + if (0 == button) + { + info->check_primary_hold_drag( + x, + y, + std::chrono::milliseconds(250), + CLICK_DISTANCE); + } + + // update info and push to UI manager + auto const released(decltype(info->buttons)(1) << button); + info->x = x; + info->y = y; + info->buttons &= ~released; + machine().ui_input().push_pointer_update( + target(), + osd::ui_event_handler::pointer::MOUSE, + info->index, + device, + x, y, + info->buttons, 0, released, info->clickcnt); +} + +void sdl_window_info::mouse_moved(unsigned device, int x, int y) +{ + if (!m_mouse_inside) + return; + + auto const info(map_pointer(SDL_FingerID(-1), device)); + if (m_active_pointers.end() == info) + return; + + // detect multi-click actions + if (BIT(info->buttons, 0)) + { + info->check_primary_hold_drag( + x, + y, + std::chrono::milliseconds(250), + CLICK_DISTANCE); + } + + // update info and push to UI manager + info->x = x; + info->y = y; + machine().ui_input().push_pointer_update( + target(), + osd::ui_event_handler::pointer::MOUSE, + info->index, + device, + x, y, + info->buttons, 0, 0, info->clickcnt); +} + +void sdl_window_info::mouse_wheel(unsigned device, int y) +{ + if (!m_mouse_inside) + return; + + auto const info(map_pointer(SDL_FingerID(-1), device)); + if (m_active_pointers.end() == info) + return; + + // push to UI manager + machine().ui_input().push_mouse_wheel_event(target(), info->x, info->y, y, 3); +} + +void sdl_window_info::finger_down(SDL_FingerID finger, unsigned device, int x, int y) +{ + auto const info(map_pointer(finger, device)); + if (m_active_pointers.end() == info) + return; + + assert(!info->buttons); + + // detect multi-click actions + info->primary_down( + x, + y, + std::chrono::milliseconds(250), + TAP_DISTANCE, + true, + m_ptrdev_info); + + // update info and push to UI manager + info->x = x; + info->y = y; + info->buttons = 1; + machine().ui_input().push_pointer_update( + target(), + osd::ui_event_handler::pointer::TOUCH, + info->index, + device, + x, y, + 1, 1, 0, info->clickcnt); +} + +void sdl_window_info::finger_up(SDL_FingerID finger, unsigned device, int x, int y) +{ + auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare)); + if ((m_active_pointers.end() == info) || (info->finger != finger)) + return; + + assert(1 == info->buttons); + + // check for conversion to a (multi-)click-and-hold/drag + info->check_primary_hold_drag( + x, + y, + std::chrono::milliseconds(250), + TAP_DISTANCE); + + // need to remember touches to recognise multi-tap gestures + if (0 < info->clickcnt) + { + auto const now(std::chrono::steady_clock::now()); + auto const time = std::chrono::milliseconds(250); + if ((time + info->pressed) >= now) + { + try + { + unsigned i(0); + if (m_ptrdev_info.size() > device) + i = m_ptrdev_info[device].clear_expired_touches(now, time); + else + m_ptrdev_info.resize(device + 1); + + if (std::size(m_ptrdev_info[device].touches) > i) + { + m_ptrdev_info[device].touches[i].when = info->pressed; + m_ptrdev_info[device].touches[i].x = info->pressedx; + m_ptrdev_info[device].touches[i].y = info->pressedy; + m_ptrdev_info[device].touches[i].cnt = info->clickcnt; + } + } + catch (std::bad_alloc const &) + { + osd_printf_error("win_window_info: error allocating pointer data\n"); + } + } + } + + // push to UI manager + machine().ui_input().push_pointer_update( + target(), + osd::ui_event_handler::pointer::TOUCH, + info->index, + device, + x, y, + 0, 0, 1, info->clickcnt); + machine().ui_input().push_pointer_leave( + target(), + osd::ui_event_handler::pointer::TOUCH, + info->index, + device, + x, y, + 0, info->clickcnt); + + // dump pointer data + m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index); + if (info->index < m_next_pointer) + m_next_pointer = info->index; + m_active_pointers.erase(info); +} + +void sdl_window_info::finger_moved(SDL_FingerID finger, unsigned device, int x, int y) +{ + auto info(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare)); + if ((m_active_pointers.end() == info) || (info->finger != finger)) + + assert(1 == info->buttons); + + if ((x != info->x) || (y != info->y)) + { + info->check_primary_hold_drag( + x, + y, + std::chrono::milliseconds(250), + TAP_DISTANCE); + + // update info and push to UI manager + info->x = x; + info->y = y; + machine().ui_input().push_pointer_update( + target(), + osd::ui_event_handler::pointer::TOUCH, + info->index, + device, + x, y, + 1, 0, 0, info->clickcnt); + } +} + //============================================================ // sdlwindow_video_window_create // (main thread) @@ -373,9 +632,9 @@ void sdl_window_info::complete_destroy() if (fullscreen() && video_config.switchres) { - SDL_SetWindowFullscreen(platform_window(), 0); // Try to set mode - SDL_SetWindowDisplayMode(platform_window(), &m_original_mode->mode); // Try to set mode - SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN); // Try to set mode + SDL_SetWindowFullscreen(platform_window(), 0); + SDL_SetWindowDisplayMode(platform_window(), &m_original_mode); + SDL_SetWindowFullscreen(platform_window(), SDL_WINDOW_FULLSCREEN); } renderer_reset(); @@ -684,7 +943,7 @@ int sdl_window_info::complete_create() SDL_DisplayMode mode; //SDL_GetCurrentDisplayMode(window().monitor()->handle, &mode); SDL_GetWindowDisplayMode(platform_window(), &mode); - m_original_mode->mode = mode; + m_original_mode = mode; mode.w = temp.width(); mode.h = temp.height(); if (m_win_config.refresh) @@ -1021,6 +1280,52 @@ osd_dim sdl_window_info::get_max_bounds(int constrain) return maximum.dim(); } + +std::vector<sdl_window_info::sdl_pointer_info>::iterator sdl_window_info::map_pointer(SDL_FingerID finger, unsigned device) +{ + auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), finger, &sdl_pointer_info::compare)); + if ((m_active_pointers.end() != found) && (found->finger == finger)) + return found; + + if ((sizeof(m_next_pointer) * 8) <= m_next_pointer) + { + assert(~decltype(m_pointer_mask)(0) == m_pointer_mask); + osd_printf_warning("sdl_window_info: exceeded maximum number of active pointers\n"); + return m_active_pointers.end(); + } + assert(!BIT(m_pointer_mask, m_next_pointer)); + + try + { + found = m_active_pointers.emplace( + found, + sdl_pointer_info(finger, m_next_pointer, device)); + m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer; + do + { + ++m_next_pointer; + } + while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer)); + + return found; + } + catch (std::bad_alloc const &) + { + osd_printf_error("sdl_window_info: error allocating pointer data\n"); + return m_active_pointers.end(); + } +} + + +inline sdl_window_info::sdl_pointer_info::sdl_pointer_info(SDL_FingerID f, unsigned i, unsigned d) + : pointer_info(i, d) + , finger(f) +{ +} + + + + //============================================================ // construction and destruction //============================================================ @@ -1040,13 +1345,18 @@ sdl_window_info::sdl_window_info( , m_extra_flags(0) , m_mouse_captured(false) , m_mouse_hidden(false) + , m_pointer_mask(0) + , m_next_pointer(0) + , m_mouse_inside(false) { //FIXME: these should be per_window in config-> or even better a bit set m_fullscreen = !video_config.windowed; m_prescale = video_config.prescale; m_windowed_dim = osd_dim(config->width, config->height); - m_original_mode = std::make_unique<SDL_DM_Wrapper>(); + + m_ptrdev_info.reserve(1); + m_active_pointers.reserve(16); } sdl_window_info::~sdl_window_info() diff --git a/src/osd/sdl/window.h b/src/osd/sdl/window.h index 77477176b51..49fe452192f 100644 --- a/src/osd/sdl/window.h +++ b/src/osd/sdl/window.h @@ -14,22 +14,20 @@ #include "modules/osdwindow.h" #include "osdsync.h" +#include <SDL2/SDL.h> + +#include <chrono> #include <cstdint> #include <memory> +#include <vector> //============================================================ // TYPE DEFINITIONS //============================================================ -struct SDL_Window; - class render_target; -// forward of SDL_DisplayMode not possible (typedef struct) - define wrapper - -class SDL_DM_Wrapper; - typedef uintptr_t HashT; #define OSDWORK_CALLBACK(name) void *name(void *param, int threadid) @@ -65,26 +63,34 @@ public: int xy_to_render_target(int x, int y, int *xt, int *yt); -private: - // window handle and info - int m_startmaximized; + void mouse_entered(unsigned device); + void mouse_left(unsigned device); + void mouse_down(unsigned device, int x, int y, unsigned button); + void mouse_up(unsigned device, int x, int y, unsigned button); + void mouse_moved(unsigned device, int x, int y); + void mouse_wheel(unsigned device, int y); + void finger_down(SDL_FingerID finger, unsigned device, int x, int y); + void finger_up(SDL_FingerID finger, unsigned device, int x, int y); + void finger_moved(SDL_FingerID finger, unsigned device, int x, int y); - // dimensions - osd_dim m_minimum_dim; - osd_dim m_windowed_dim; +private: + struct sdl_pointer_info : public pointer_info + { + static constexpr bool compare(sdl_pointer_info const &info, SDL_FingerID finger) { return info.finger < finger; } - // rendering info - osd_event m_rendered_event; + sdl_pointer_info(sdl_pointer_info const &) = default; + sdl_pointer_info(sdl_pointer_info &&) = default; + sdl_pointer_info &operator=(sdl_pointer_info const &) = default; + sdl_pointer_info &operator=(sdl_pointer_info &&) = default; - // Original display_mode - std::unique_ptr<SDL_DM_Wrapper> m_original_mode; + sdl_pointer_info(SDL_FingerID, unsigned i, unsigned d); - int m_extra_flags; + SDL_FingerID finger; + }; // returns 0 on success, else 1 int complete_create(); -private: int wnd_extra_width(); int wnd_extra_height(); osd_rect constrain_to_aspect_ratio(const osd_rect &rect, int adjustment); @@ -94,12 +100,35 @@ private: osd_dim pick_best_mode(); void set_fullscreen(int afullscreen) { m_fullscreen = afullscreen; } - // monitor info - bool m_mouse_captured; - bool m_mouse_hidden; - void measure_fps(int update); + std::vector<sdl_pointer_info>::iterator map_pointer(SDL_FingerID finger, unsigned device); + + // window handle and info + int m_startmaximized; + + // dimensions + osd_dim m_minimum_dim; + osd_dim m_windowed_dim; + + // rendering info + osd_event m_rendered_event; + + // Original display_mode + SDL_DisplayMode m_original_mode; + + int m_extra_flags; + + // monitor info + bool m_mouse_captured; + bool m_mouse_hidden; + + // info on currently active pointers - 64 pointers ought to be enough for anyone + uint64_t m_pointer_mask; + unsigned m_next_pointer; + bool m_mouse_inside; + std::vector<pointer_dev_info> m_ptrdev_info; + std::vector<sdl_pointer_info> m_active_pointers; }; #endif // MAME_OSD_SDL_WINDOW_H diff --git a/src/osd/strconv.cpp b/src/osd/strconv.cpp index 737cb9fc1dc..130f259d70f 100644 --- a/src/osd/strconv.cpp +++ b/src/osd/strconv.cpp @@ -5,15 +5,19 @@ // strconv.cpp - Win32 string conversion // //============================================================ -#if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) -#include <windows.h> -#endif + +#include "strconv.h" + +#include "unicode.h" + #include <algorithm> #include <cassert> -// MAMEOS headers -#include "strconv.h" -#if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) + +#if defined(_WIN32) + +#include <windows.h> + namespace osd::text { @@ -254,20 +258,22 @@ std::string from_wstring(const WCHAR *s) int osd_uchar_from_osdchar(char32_t *uchar, const char *osdchar, size_t count) { - // FIXME: Does not handle charsets that use variable lengths encodings such - // as example GB18030 or UTF-8. - // FIXME: Assumes all characters can be converted into a single wchar_t - // which may not always be the case such as with surrogate pairs. - - WCHAR wch; + // handle UTF-8 internally + if (GetACP() == CP_UTF8) + return uchar_from_utf8(uchar, osdchar, count); + + // FIXME: This makes multiple bad assumptions: + // * Assumes any character can be converted to a single wchar_t. + // * Assumes characters are either one byte or maximum number of bytes. + // * Doesn't handle nominal single-byte encodings with combining characters. CPINFO cp; - if (!GetCPInfo(CP_ACP, &cp)) goto error; // The multibyte char can't be bigger than the max character size count = std::min(count, size_t(IsDBCSLeadByte(*osdchar) ? cp.MaxCharSize : 1)); + WCHAR wch; if (MultiByteToWideChar(CP_ACP, 0, osdchar, static_cast<DWORD>(count), &wch, 1) == 0) goto error; @@ -280,23 +286,35 @@ error: } -#else -#include "unicode.h" +#else // _WIN32 + //============================================================ // osd_uchar_from_osdchar //============================================================ int osd_uchar_from_osdchar(char32_t *uchar, const char *osdchar, size_t count) { + // TODO: should this handle count == 0? + if (!*osdchar) + { + // mbstowcs stops on encountering NUL and doesn't include it in the output count + *uchar = char32_t(0); + return 1; + } + + // FIXME: mbstowcs depends on global state wchar_t wch; - count = mbstowcs(&wch, (char *)osdchar, 1); - if (count != -1) + if (count != size_t(-1)) + { *uchar = wch; + return int(count); + } else + { *uchar = 0; - - return count; + return -1; + } } -#endif +#endif // _WIN32 diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp index 387a7f6daba..2759b32227f 100644 --- a/src/osd/windows/window.cpp +++ b/src/osd/windows/window.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb //============================================================ // // window.cpp - Win32 window handling @@ -31,10 +31,10 @@ #include "modules/monitor/monitor_common.h" -#define NOT_ALREADY_DOWN(x) (x & 0x40000000) == 0 +#define NOT_ALREADY_DOWN(x) ((x & 0x40000000) == 0) #define SCAN_CODE(x) ((x >> 16) & 0xff) -#define IS_EXTENDED(x) (0x01000000 & x) -#define MAKE_DI_SCAN(scan, isextended) (scan & 0x7f) | (isextended ? 0x80 : 0x00) +#define IS_EXTENDED(x) ((x >> 24) & 0x01) +#define MAKE_DI_SCAN(scan, isextended) ((scan & 0x7f) | (isextended ? 0x80 : 0x00)) #define WINOSD(machine) downcast<windows_osd_interface*>(&machine.osd()) //============================================================ @@ -64,6 +64,34 @@ #define WM_USER_SET_MINSIZE (WM_USER + 5) +namespace { + +// If legacy mouse to pointer event translation is enabled, translated +// WM_POINTER* events have pointer ID zero. Assume this will never be +// seen for "real" pointer events. +constexpr WORD MOUSE_POINTER_ID = 0; + +constexpr unsigned get_pointer_buttons(WPARAM wparam) +{ + return + (IS_POINTER_FIRSTBUTTON_WPARAM(wparam) ? 0x01 : 0x00) | + (IS_POINTER_SECONDBUTTON_WPARAM(wparam) ? 0x02 : 0x00) | + (IS_POINTER_THIRDBUTTON_WPARAM(wparam) ? 0x04 : 0x00) | + (IS_POINTER_FOURTHBUTTON_WPARAM(wparam) ? 0x08 : 0x00) | + (IS_POINTER_FIFTHBUTTON_WPARAM(wparam) ? 0x10 : 0x00); +} + +constexpr osd::ui_event_handler::pointer convert_pointer_type(POINTER_INPUT_TYPE type) +{ + // PT_POINTER isn't a real type, and we'll leave PT_TOUCHPAD as unknown for now + return + (PT_TOUCH == type) ? osd::ui_event_handler::pointer::TOUCH : + (PT_PEN == type) ? osd::ui_event_handler::pointer::PEN : + (PT_MOUSE == type) ? osd::ui_event_handler::pointer::MOUSE : + osd::ui_event_handler::pointer::UNKNOWN; +} + +} // anonymous namespace //============================================================ // GLOBAL VARIABLES @@ -123,39 +151,12 @@ bool windows_osd_interface::window_init() return true; } -void windows_osd_interface::update_slider_list() -{ - for (const auto &window : osd_common_t::window_list()) - { - // check if any window has dirty sliders - if (window->has_renderer() && window->renderer().sliders_dirty()) - { - build_slider_list(); - return; - } - } -} int windows_osd_interface::window_count() { return osd_common_t::window_list().size(); } -void windows_osd_interface::build_slider_list() -{ - m_sliders.clear(); - - for (const auto &window : osd_common_t::window_list()) - { - if (window->has_renderer()) - { - // take the sliders of the first window - std::vector<ui::menu_item> window_sliders = window->renderer().get_slider_list(); - m_sliders.insert(m_sliders.end(), window_sliders.begin(), window_sliders.end()); - } - } -} - void windows_osd_interface::add_audio_to_recording(const int16_t *buffer, int samples_this_frame) { auto const &window = osd_common_t::window_list().front(); // We only record on the first window @@ -163,6 +164,7 @@ void windows_osd_interface::add_audio_to_recording(const int16_t *buffer, int sa window->renderer().add_audio_to_recording(buffer, samples_this_frame); } + //============================================================ // winwindow_exit // (main thread) @@ -190,6 +192,14 @@ void windows_osd_interface::window_exit() } +inline win_window_info::win_pointer_info::win_pointer_info(WORD p, POINTER_INPUT_TYPE t, unsigned i, unsigned d) + : pointer_info(i, d) + , ptrid(p) + , type(t) +{ +} + + win_window_info::win_window_info( running_machine &machine, render_module &renderprovider, @@ -216,6 +226,10 @@ win_window_info::win_window_info( , m_resize_state(RESIZE_STATE_NORMAL) , m_main(nullptr) , m_attached_mode(false) + , m_pointer_mask(0) + , m_next_pointer(0) + , m_next_ptrdev(0) + , m_tracking_mouse(false) { m_non_fullscreen_bounds.left = 0; m_non_fullscreen_bounds.top = 0; @@ -224,6 +238,10 @@ win_window_info::win_window_info( m_fullscreen = !video_config.windowed; m_prescale = video_config.prescale; + + m_ptrdev_map.reserve(8); + m_ptrdev_info.reserve(1); + m_active_pointers.reserve(16); } POINT win_window_info::s_saved_cursor_pos = { -1, -1 }; @@ -258,7 +276,7 @@ void win_window_info::show_pointer() s_saved_cursor_pos.x = s_saved_cursor_pos.y = -1; } - while (ShowCursor(TRUE) < 1) {}; + while (ShowCursor(TRUE) < 1) { } ShowCursor(FALSE); } @@ -303,7 +321,7 @@ static LRESULT CALLBACK winwindow_video_window_proc_ui(HWND wnd, UINT message, W // is_mame_window //============================================================ -static bool is_mame_window(HWND hwnd) +inline bool is_mame_window(HWND hwnd) { for (const auto &window : osd_common_t::window_list()) if (dynamic_cast<win_window_info &>(*window).platform_window() == hwnd) @@ -312,30 +330,50 @@ static bool is_mame_window(HWND hwnd) return false; } -inline static BOOL handle_mouse_button(windows_osd_interface *osd, int button, int down, int x, int y) +inline BOOL handle_mouse_button(windows_osd_interface &osd, int button, int down, LPARAM lparam) { - MouseButtonEventArgs args; - args.button = button; - args.keydown = down; - args.xpos = x; - args.ypos = y; + MouseUpdateEventArgs args; + args.pressed = (down ? 1 : 0) << button; + args.released = (down ? 0 : 1) << button; + args.vdelta = 0; + args.hdelta = 0; + args.xpos = GET_X_LPARAM(lparam); + args.ypos = GET_Y_LPARAM(lparam); - bool handled = osd->handle_input_event(INPUT_EVENT_MOUSE_BUTTON, &args); + bool const handled = osd.handle_input_event(INPUT_EVENT_MOUSE_BUTTON, &args); // When in lightgun mode or mouse mode, the mouse click may be routed to the input system // because the mouse interactions in the UI are routed from the video_window_proc below // we need to make sure they aren't suppressed in these cases. - return handled && !osd->options().lightgun() && !osd->options().mouse(); + return handled && !osd.options().lightgun() && !osd.options().mouse(); } -inline static BOOL handle_keypress(windows_osd_interface *osd, int vkey, int down, int scancode, BOOL extended_key) +inline BOOL handle_mouse_wheel(windows_osd_interface &osd, int v, int h, LPARAM lparam) +{ + MouseUpdateEventArgs args; + args.pressed = 0; + args.released = 0; + args.vdelta = v; + args.hdelta = h; + args.xpos = GET_X_LPARAM(lparam); + args.ypos = GET_Y_LPARAM(lparam); + + bool const handled = osd.handle_input_event(INPUT_EVENT_MOUSE_WHEEL, &args); + + // When in lightgun mode or mouse mode, the mouse wheel may be routed to the input system + // because the mouse interactions in the UI are routed from the video_window_proc below + // we need to make sure they aren't suppressed in these cases. + return handled && !osd.options().lightgun() && !osd.options().mouse(); +} + +inline BOOL handle_keypress(windows_osd_interface &osd, int vkey, int down, LPARAM lparam) { KeyPressEventArgs args; args.event_id = down ? INPUT_EVENT_KEYDOWN : INPUT_EVENT_KEYUP; - args.scancode = MAKE_DI_SCAN(scancode, extended_key); + args.scancode = MAKE_DI_SCAN(SCAN_CODE(lparam), IS_EXTENDED(lparam)); args.vkey = vkey; - return osd->handle_input_event(args.event_id, &args); + return osd.handle_input_event(args.event_id, &args); } //============================================================ @@ -376,52 +414,62 @@ void windows_osd_interface::process_events(bool ingame, bool nodispatch) // forward mouse button downs to the input system case WM_LBUTTONDOWN: - dispatch = !handle_mouse_button(this, 0, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, 0, TRUE, message.lParam); break; case WM_RBUTTONDOWN: - dispatch = !handle_mouse_button(this, 1, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, 1, TRUE, message.lParam); break; case WM_MBUTTONDOWN: - dispatch = !handle_mouse_button(this, 2, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, 2, TRUE, message.lParam); break; case WM_XBUTTONDOWN: - dispatch = !handle_mouse_button(this, 3, TRUE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, (GET_XBUTTON_WPARAM(message.wParam) == XBUTTON1) ? 3 : 4, TRUE, message.lParam); break; // forward mouse button ups to the input system case WM_LBUTTONUP: - dispatch = !handle_mouse_button(this, 0, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, 0, FALSE, message.lParam); break; case WM_RBUTTONUP: - dispatch = !handle_mouse_button(this, 1, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, 1, FALSE, message.lParam); break; case WM_MBUTTONUP: - dispatch = !handle_mouse_button(this, 2, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, 2, FALSE, message.lParam); break; case WM_XBUTTONUP: - dispatch = !handle_mouse_button(this, 3, FALSE, GET_X_LPARAM(message.lParam), GET_Y_LPARAM(message.lParam)); + dispatch = !handle_mouse_button(*this, (GET_XBUTTON_WPARAM(message.wParam) == XBUTTON1) ? 3 : 4, FALSE, message.lParam); + break; + + // forward mouse wheel movement to the input system + case WM_MOUSEWHEEL: + dispatch = !handle_mouse_wheel(*this, GET_WHEEL_DELTA_WPARAM(message.wParam), 0, message.lParam); break; + case WM_MOUSEHWHEEL: + dispatch = !handle_mouse_wheel(*this, 0, GET_WHEEL_DELTA_WPARAM(message.wParam), message.lParam); + break; + + // forward keystrokes to the input system case WM_KEYDOWN: if (NOT_ALREADY_DOWN(message.lParam)) - dispatch = !handle_keypress(this, message.wParam, TRUE, SCAN_CODE(message.lParam), IS_EXTENDED(message.lParam)); + dispatch = !handle_keypress(*this, message.wParam, TRUE, message.lParam); break; case WM_KEYUP: - dispatch = !handle_keypress(this, message.wParam, FALSE, SCAN_CODE(message.lParam), IS_EXTENDED(message.lParam)); + dispatch = !handle_keypress(*this, message.wParam, FALSE, message.lParam); break; } } // dispatch if necessary if (dispatch) - winwindow_dispatch_message(machine(), &message); + winwindow_dispatch_message(machine(), message); } } while (ui_temp_pause > 0); @@ -437,12 +485,12 @@ void windows_osd_interface::process_events(bool ingame, bool nodispatch) // (main thread) //============================================================ -void winwindow_dispatch_message(running_machine &machine, MSG *message) +void winwindow_dispatch_message(running_machine &machine, MSG const &message) { assert(GetCurrentThreadId() == main_threadid); // dispatch our special communication messages - switch (message->message) + switch (message.message) { // special case for quit case WM_QUIT: @@ -451,8 +499,8 @@ void winwindow_dispatch_message(running_machine &machine, MSG *message) // everything else dispatches normally default: - TranslateMessage(message); - DispatchMessage(message); + TranslateMessage(&message); + DispatchMessage(&message); break; } } @@ -1066,49 +1114,7 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR case WM_SYSKEYDOWN: break; - // input events - case WM_MOUSEMOVE: - window->machine().ui_input().push_mouse_move_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); - break; - - case WM_MOUSELEAVE: - window->machine().ui_input().push_mouse_leave_event(window->target()); - break; - - case WM_LBUTTONDOWN: - { - auto const ticks = std::chrono::steady_clock::now(); - window->machine().ui_input().push_mouse_down_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); - - // check for a double-click - avoid overflow by adding times rather than subtracting - if (ticks < (window->m_lastclicktime + std::chrono::milliseconds(GetDoubleClickTime())) && - GET_X_LPARAM(lparam) >= window->m_lastclickx - 4 && GET_X_LPARAM(lparam) <= window->m_lastclickx + 4 && - GET_Y_LPARAM(lparam) >= window->m_lastclicky - 4 && GET_Y_LPARAM(lparam) <= window->m_lastclicky + 4) - { - window->m_lastclicktime = std::chrono::steady_clock::time_point::min(); - window->machine().ui_input().push_mouse_double_click_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); - } - else - { - window->m_lastclicktime = ticks; - window->m_lastclickx = GET_X_LPARAM(lparam); - window->m_lastclicky = GET_Y_LPARAM(lparam); - } - } - break; - - case WM_LBUTTONUP: - window->machine().ui_input().push_mouse_up_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); - break; - - case WM_RBUTTONDOWN: - window->machine().ui_input().push_mouse_rdown_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); - break; - - case WM_RBUTTONUP: - window->machine().ui_input().push_mouse_rup_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); - break; - + // text input events case WM_CHAR: { char16_t const ch = char16_t(wparam); @@ -1140,14 +1146,71 @@ LRESULT CALLBACK win_window_info::video_window_proc(HWND wnd, UINT message, WPAR window->machine().ui_input().push_char_event(window->target(), char32_t(wparam)); break; + // legacy mouse events + case WM_MOUSEMOVE: + window->mouse_updated(wparam, lparam); + break; + + case WM_MOUSELEAVE: + window->mouse_left(wparam, lparam); + break; + + case WM_LBUTTONDOWN: + window->mouse_updated(wparam, lparam); + break; + + case WM_LBUTTONUP: + window->mouse_updated(wparam, lparam); + break; + + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + window->mouse_updated(wparam, lparam); + break; + + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + window->mouse_updated(wparam, lparam); + return TRUE; + case WM_MOUSEWHEEL: { + POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + ScreenToClient(wnd, &where); UINT ucNumLines = 3; // default SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ucNumLines, 0); - window->machine().ui_input().push_mouse_wheel_event(window->target(), GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), GET_WHEEL_DELTA_WPARAM(wparam), ucNumLines); + window->machine().ui_input().push_mouse_wheel_event(window->target(), where.x, where.y, GET_WHEEL_DELTA_WPARAM(wparam), ucNumLines); } break; + // new-style pointer handling (mouse/pen/touch) + case WM_POINTERENTER: + window->pointer_entered(wparam, lparam); + break; + case WM_POINTERLEAVE: + window->pointer_left(wparam, lparam); + break; + case WM_POINTERDOWN: + case WM_POINTERUP: + case WM_POINTERUPDATE: + window->pointer_updated(wparam, lparam); + break; + case WM_POINTERCAPTURECHANGED: + window->pointer_capture_changed(wparam, lparam); + break; + // TODO: other pointer events? + //case WM_POINTERACTIVATE: + //case WM_POINTERDEVICECHANGE: + //case WM_POINTERDEVICEINRANGE: + //case WM_POINTERDEVICEOUTOFRANGE: + //case WM_POINTERROUTEDAWAY: + //case WM_POINTERROUTEDRELEASED: + //case WM_POINTERROUTEDTO: + //case WM_POINTERWHEEL: + //case WM_POINTERHWHEEL: + // pause the system when we start a menu or resize case WM_ENTERSIZEMOVE: window->m_resize_state = RESIZE_STATE_RESIZING; @@ -1834,24 +1897,412 @@ win_window_focus win_window_info::focus() const } +void win_window_info::pointer_entered(WPARAM wparam, LPARAM lparam) +{ + assert(!IS_POINTER_CANCELED_WPARAM(wparam)); + auto const info(map_pointer(wparam)); + if (m_active_pointers.end() != info) + { + POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + ScreenToClient(platform_window(), &where); + info->x = where.x; + info->y = where.y; + machine().ui_input().push_pointer_update( + target(), + convert_pointer_type(info->type), + info->index, + info->device, + where.x, where.y, + 0U, 0U, 0U, 0U); + } +} + +void win_window_info::pointer_left(WPARAM wparam, LPARAM lparam) +{ + auto const info(find_pointer(wparam)); + if (m_active_pointers.end() != info) + { + bool const canceled(IS_POINTER_CANCELED_WPARAM(wparam)); + POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + ScreenToClient(platform_window(), &where); + expire_pointer(info, where, canceled); + } +} + +void win_window_info::pointer_updated(WPARAM wparam, LPARAM lparam) +{ + bool const canceled(IS_POINTER_CANCELED_WPARAM(wparam)); + auto const info(canceled ? find_pointer(wparam) : map_pointer(wparam)); + if (m_active_pointers.end() != info) + { + POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + ScreenToClient(platform_window(), &where); + unsigned const buttons(canceled ? 0 : get_pointer_buttons(wparam)); + update_pointer(*info, where, buttons, canceled); + } +} + +void win_window_info::pointer_capture_changed(WPARAM wparam, LPARAM lparam) +{ + auto const info(find_pointer(wparam)); + if (m_active_pointers.end() != info) + { + // treat this as the pointer being stolen - fail any gestures + if (BIT(info->buttons, 0) && (0 < info->clickcnt)) + info->clickcnt = -info->clickcnt; + + // push to UI manager and dump pointer data + machine().ui_input().push_pointer_abort( + target(), + convert_pointer_type(info->type), + info->index, + info->device, + info->x, info->y, + info->buttons, info->clickcnt); + m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index); + if (info->index < m_next_pointer) + m_next_pointer = info->index; + m_active_pointers.erase(info); + } +} + +void win_window_info::mouse_left(WPARAM wparam, LPARAM lparam) +{ + m_tracking_mouse = false; + auto const info(find_mouse_pointer()); + if (m_active_pointers.end() != info) + { + POINT where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + expire_pointer(info, where, false); + } +} + +void win_window_info::mouse_updated(WPARAM wparam, LPARAM lparam) +{ + auto const info(map_mouse_pointer()); + if (m_active_pointers.end() == info) + return; + + POINT const where{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) }; + unsigned const buttons( + ((MK_LBUTTON & wparam) ? 0x01 : 0x00) | + ((MK_RBUTTON & wparam) ? 0x02 : 0x00) | + ((MK_MBUTTON & wparam) ? 0x04 : 0x00) | + ((MK_XBUTTON1 & wparam) ? 0x08 : 0x00) | + ((MK_XBUTTON2 & wparam) ? 0x10 : 0x00)); + + // continue to track the mouse outside the window if buttons are down + if (buttons && !info->buttons) + { + SetCapture(platform_window()); + + TRACKMOUSEEVENT tm; + std::memset(&tm, 0, sizeof(tm)); + tm.cbSize = sizeof(tm); + tm.dwFlags = TME_CANCEL | TME_LEAVE; + tm.hwndTrack = platform_window(); + TrackMouseEvent(&tm); + m_tracking_mouse = false; + } + else + { + if (!buttons && info->buttons) + { + if (GetCapture() == platform_window()) + ReleaseCapture(); + } + + if (!m_tracking_mouse) + { + TRACKMOUSEEVENT tm; + std::memset(&tm, 0, sizeof(tm)); + tm.cbSize = sizeof(tm); + tm.dwFlags = TME_LEAVE; + tm.hwndTrack = platform_window(); + TrackMouseEvent(&tm); + m_tracking_mouse = true; + } + } + + update_pointer(*info, where, buttons, false); +} + +void win_window_info::expire_pointer(std::vector<win_pointer_info>::iterator info, POINT const &where, bool canceled) +{ + // leaving implicitly releases buttons, so check hold/drag if necessary + if (BIT(info->buttons, 0) && (0 < info->clickcnt)) + { + if (!canceled) + { + auto const now(std::chrono::steady_clock::now()); + auto const exp(std::chrono::milliseconds(GetDoubleClickTime()) + info->pressed); + int const dx(where.x - info->pressedx); + int const dy(where.y - info->pressedy); + int const distance((dx * dx) + (dy * dy)); + int const tolerance((PT_TOUCH == info->type) ? TAP_DISTANCE : CLICK_DISTANCE); + if ((exp < now) || (tolerance < distance)) + info->clickcnt = -info->clickcnt; + } + else + { + info->clickcnt = -info->clickcnt; + } + } + + // need to remember touches to recognise multi-tap gestures + if (!canceled && (PT_TOUCH == info->type) && (0 < info->clickcnt)) + { + auto const now(std::chrono::steady_clock::now()); + auto const time = std::chrono::milliseconds(GetDoubleClickTime()); + if ((time + info->pressed) >= now) + { + try + { + unsigned i(0); + if (m_ptrdev_info.size() > info->device) + i = m_ptrdev_info[info->device].clear_expired_touches(now, time); + else + m_ptrdev_info.resize(info->device + 1); + + if (std::size(m_ptrdev_info[info->device].touches) > i) + { + m_ptrdev_info[info->device].touches[i].when = info->pressed; + m_ptrdev_info[info->device].touches[i].x = info->pressedx; + m_ptrdev_info[info->device].touches[i].y = info->pressedy; + m_ptrdev_info[info->device].touches[i].cnt = info->clickcnt; + } + } + catch (std::bad_alloc const &) + { + osd_printf_error("win_window_info: error allocating pointer data\n"); + } + } + } + + // push to UI manager and dump pointer data + if (!canceled) + { + machine().ui_input().push_pointer_leave( + target(), + convert_pointer_type(info->type), + info->index, + info->device, + where.x, where.y, + info->buttons, info->clickcnt); + } + else + { + machine().ui_input().push_pointer_abort( + target(), + convert_pointer_type(info->type), + info->index, + info->device, + where.x, where.y, + info->buttons, info->clickcnt); + } + m_pointer_mask &= ~(decltype(m_pointer_mask)(1) << info->index); + if (info->index < m_next_pointer) + m_next_pointer = info->index; + m_active_pointers.erase(info); +} + +void win_window_info::update_pointer(win_pointer_info &info, POINT const &where, unsigned buttons, bool canceled) +{ + if (!canceled && (where.x == info.x) && (where.y == info.y) && (buttons == info.buttons)) + return; + + // detect multi-click actions + unsigned const pressed(canceled ? 0 : (buttons & ~info.buttons)); + unsigned const released(canceled ? ~info.buttons : (~buttons & info.buttons)); + if (BIT(pressed, 0)) + { + info.primary_down( + where.x, where.y, + std::chrono::milliseconds(GetDoubleClickTime()), + (PT_TOUCH == info.type) ? TAP_DISTANCE : CLICK_DISTANCE, + PT_TOUCH == info.type, + m_ptrdev_info); + } + else if (BIT(info.buttons, 0)) + { + info.check_primary_hold_drag( + where.x, where.y, + std::chrono::milliseconds(GetDoubleClickTime()), + (PT_TOUCH == info.type) ? TAP_DISTANCE : CLICK_DISTANCE); + } + + // update info and push to UI manager + info.x = where.x; + info.y = where.y; + info.buttons = buttons; + if (!canceled) + { + machine().ui_input().push_pointer_update( + target(), + convert_pointer_type(info.type), + info.index, + info.device, + where.x, where.y, + buttons, pressed, released, info.clickcnt); + } + else + { + machine().ui_input().push_pointer_abort( + target(), + convert_pointer_type(info.type), + info.index, + info.device, + where.x, where.y, + released, info.clickcnt); + } +} + +std::vector<win_window_info::win_pointer_info>::iterator win_window_info::map_pointer(WPARAM wparam) +{ + WORD const ptrid(GET_POINTERID_WPARAM(wparam)); + auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare)); + if ((m_active_pointers.end() != found) && (found->ptrid == ptrid)) + return found; + + if ((sizeof(m_next_pointer) * 8) <= m_next_pointer) + { + assert(~decltype(m_pointer_mask)(0) == m_pointer_mask); + osd_printf_warning("win_window_info: exceeded maximum number of active pointers\n"); + return m_active_pointers.end(); + } + assert(!BIT(m_pointer_mask, m_next_pointer)); + + POINTER_INFO info = { 0 }; + if (!OSD_DYNAMIC_CALL(GetPointerInfo, ptrid, &info)) + { + osd_printf_error("win_window_info: failed to get info for pointer ID %u\n", ptrid); + return m_active_pointers.end(); + } + + auto devpos(std::lower_bound( + m_ptrdev_map.begin(), + m_ptrdev_map.end(), + info.sourceDevice, + [] (std::pair<HANDLE, unsigned> const &mapping, HANDLE device) + { + return mapping.first < device; + })); + + try + { + if ((m_ptrdev_map.end() == devpos) || (devpos->first != info.sourceDevice)) + { + devpos = m_ptrdev_map.emplace(devpos, info.sourceDevice, m_next_ptrdev); + ++m_next_ptrdev; + } + + found = m_active_pointers.emplace( + found, + win_pointer_info(ptrid, info.pointerType, m_next_pointer, devpos->second)); + m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer; + do + { + ++m_next_pointer; + } + while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer)); + + return found; + } + catch (std::bad_alloc const &) + { + osd_printf_error("win_window_info: error allocating pointer data\n"); + return m_active_pointers.end(); + } +} + +std::vector<win_window_info::win_pointer_info>::iterator win_window_info::find_pointer(WPARAM wparam) +{ + WORD const ptrid(GET_POINTERID_WPARAM(wparam)); + auto const found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), ptrid, &win_pointer_info::compare)); + if ((m_active_pointers.end() != found) && (found->ptrid == ptrid)) + return found; + else + return m_active_pointers.end(); +} + +std::vector<win_window_info::win_pointer_info>::iterator win_window_info::map_mouse_pointer() +{ + auto found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), MOUSE_POINTER_ID, &win_pointer_info::compare)); + if ((m_active_pointers.end() != found) && (found->ptrid == MOUSE_POINTER_ID)) + return found; + + if ((sizeof(m_next_pointer) * 8) <= m_next_pointer) + { + assert(~decltype(m_pointer_mask)(0) == m_pointer_mask); + osd_printf_warning("win_window_info: exceeded maximum number of active pointers\n"); + return m_active_pointers.end(); + } + assert(!BIT(m_pointer_mask, m_next_pointer)); + + auto devpos(std::lower_bound( + m_ptrdev_map.begin(), + m_ptrdev_map.end(), + INVALID_HANDLE_VALUE, + [] (std::pair<HANDLE, unsigned> const &mapping, HANDLE device) + { + return mapping.first < device; + })); + + try + { + if ((m_ptrdev_map.end() == devpos) || (devpos->first != INVALID_HANDLE_VALUE)) + { + devpos = m_ptrdev_map.emplace(devpos, INVALID_HANDLE_VALUE, m_next_ptrdev); + ++m_next_ptrdev; + } + + found = m_active_pointers.emplace( + found, + win_pointer_info(MOUSE_POINTER_ID, PT_MOUSE, m_next_pointer, devpos->second)); + m_pointer_mask |= decltype(m_pointer_mask)(1) << m_next_pointer; + do + { + ++m_next_pointer; + } + while (((sizeof(m_next_pointer) * 8) > m_next_pointer) && BIT(m_pointer_mask, m_next_pointer)); + + return found; + } + catch (std::bad_alloc const &) + { + osd_printf_error("win_window_info: error allocating pointer data\n"); + return m_active_pointers.end(); + } +} + +std::vector<win_window_info::win_pointer_info>::iterator win_window_info::find_mouse_pointer() +{ + auto const found(std::lower_bound(m_active_pointers.begin(), m_active_pointers.end(), MOUSE_POINTER_ID, &win_pointer_info::compare)); + if ((m_active_pointers.end() != found) && (found->ptrid == MOUSE_POINTER_ID)) + return found; + else + return m_active_pointers.end(); +} + + +#if (USE_QTDEBUG) //============================================================ // winwindow_qt_filter //============================================================ -#if (USE_QTDEBUG) bool winwindow_qt_filter(void *message) { - MSG *msg = (MSG *)message; + MSG *const msg = reinterpret_cast<MSG *>(message); - if(is_mame_window(msg->hwnd) || (!msg->hwnd && (msg->message >= WM_USER))) + if (is_mame_window(msg->hwnd) || (!msg->hwnd && (msg->message >= WM_USER))) { LONG_PTR ptr; - if(msg->hwnd) // get the machine associated with this window + if (msg->hwnd) // get the machine associated with this window ptr = GetWindowLongPtr(msg->hwnd, GWLP_USERDATA); else // any one will have to do ptr = (LONG_PTR)osd_common_t::window_list().front().get(); - winwindow_dispatch_message(((win_window_info *)ptr)->machine(), msg); + winwindow_dispatch_message(reinterpret_cast<win_window_info *>(ptr)->machine(), *msg); return true; } return false; diff --git a/src/osd/windows/window.h b/src/osd/windows/window.h index 53021e43a78..ada71f1cba7 100644 --- a/src/osd/windows/window.h +++ b/src/osd/windows/window.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb //============================================================ // // window.h - Win32 window handling @@ -128,6 +128,21 @@ public: int m_resize_state; private: + struct win_pointer_info : public pointer_info + { + static constexpr bool compare(win_pointer_info const &info, WORD ptrid) { return info.ptrid < ptrid; } + + win_pointer_info(win_pointer_info const &) = default; + win_pointer_info(win_pointer_info &&) = default; + win_pointer_info &operator=(win_pointer_info const &) = default; + win_pointer_info &operator=(win_pointer_info &&) = default; + + win_pointer_info(WORD p, POINTER_INPUT_TYPE t, unsigned i, unsigned d); + + WORD ptrid; + POINTER_INPUT_TYPE type; + }; + void draw_video_contents(HDC dc, bool update); int complete_create(); int wnd_extra_width(); @@ -141,9 +156,38 @@ private: void adjust_window_position_after_major_change(); void set_fullscreen(int fullscreen); + // pointer handling helpers + void pointer_entered(WPARAM wparam, LPARAM lparam); + void pointer_left(WPARAM wparam, LPARAM lparam); + void pointer_updated(WPARAM wparam, LPARAM lparam); + void pointer_capture_changed(WPARAM wparam, LPARAM lparam); + void mouse_left(WPARAM wparam, LPARAM lparam); + void mouse_updated(WPARAM wparam, LPARAM lparam); + void expire_pointer(std::vector<win_pointer_info>::iterator info, POINT const &where, bool canceled); + void update_pointer(win_pointer_info &info, POINT const &where, unsigned buttons, bool canceled); + std::vector<win_pointer_info>::iterator map_pointer(WPARAM wparam); + std::vector<win_pointer_info>::iterator find_pointer(WPARAM wparam); + std::vector<win_pointer_info>::iterator map_mouse_pointer(); + std::vector<win_pointer_info>::iterator find_mouse_pointer(); + win_window_info * m_main; bool m_attached_mode; + // these functions first appear in Windows 8/Server 2012 + OSD_DYNAMIC_API(user32, "User32.dll", "User32.dll"); + OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerType, UINT32, POINTER_INPUT_TYPE *); + OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerInfo, UINT32, POINTER_INFO *); + OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerPenInfo, UINT32, POINTER_PEN_INFO *); + OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, GetPointerTouchInfo, UINT32, POINTER_TOUCH_INFO *); + + // info on currently active pointers - 64 pointers ought to be enough for anyone + uint64_t m_pointer_mask; + unsigned m_next_pointer, m_next_ptrdev; + bool m_tracking_mouse; + std::vector<std::pair<HANDLE, unsigned> > m_ptrdev_map; + std::vector<pointer_dev_info> m_ptrdev_info; + std::vector<win_pointer_info> m_active_pointers; + static POINT s_saved_cursor_pos; }; @@ -163,7 +207,7 @@ void winwindow_toggle_fsfx(void); void winwindow_ui_pause(running_machine &machine, int pause); int winwindow_ui_is_paused(running_machine &machine); -void winwindow_dispatch_message(running_machine &machine, MSG *message); +void winwindow_dispatch_message(running_machine &machine, MSG const &message); diff --git a/src/osd/windows/winmain.cpp b/src/osd/windows/winmain.cpp index f1df872456f..2f7df659f44 100644 --- a/src/osd/windows/winmain.cpp +++ b/src/osd/windows/winmain.cpp @@ -21,6 +21,7 @@ #include "winutil.h" #include "winfile.h" #include "modules/diagnostics/diagnostics_module.h" +#include "modules/lib/osdlib.h" #include "modules/monitor/monitor_common.h" // standard C headers @@ -180,6 +181,17 @@ int main(int argc, char *argv[]) if (!isatty(fileno(stderr))) setvbuf(stderr, (char *) nullptr, _IOFBF, 64); + { + // Disable legacy mouse to pointer event translation - it's broken: + // * No WM_POINTERLEAVE event when mouse pointer moves directly to an + // overlapping window from the same process. + // * Still receive occasional WM_MOUSEMOVE events. + OSD_DYNAMIC_API(user32, "User32.dll", "User32.dll"); + OSD_DYNAMIC_API_FN(user32, BOOL, WINAPI, EnableMouseInPointer, BOOL); + if (OSD_DYNAMIC_API_TEST(EnableMouseInPointer)) + OSD_DYNAMIC_CALL(EnableMouseInPointer, FALSE); + } + // initialize common controls InitCommonControls(); @@ -340,12 +352,6 @@ void windows_osd_interface::init(running_machine &machine) // initialize the subsystems osd_common_t::init_subsystems(); - // notify listeners of screen configuration - for (const auto &info : osd_common_t::window_list()) - { - machine.output().set_value(string_format("Orientation(%s)", info->monitor()->devicename()), dynamic_cast<win_window_info &>(*info).m_targetorient); - } - // hook up the debugger log if (options.oslog()) machine.add_logerror_callback(&windows_osd_interface::output_oslog); diff --git a/src/osd/windows/winmain.h b/src/osd/windows/winmain.h index bb96ec2a3e4..5b8e992be6b 100644 --- a/src/osd/windows/winmain.h +++ b/src/osd/windows/winmain.h @@ -30,7 +30,8 @@ enum input_event INPUT_EVENT_RAWINPUT, INPUT_EVENT_ARRIVAL, INPUT_EVENT_REMOVAL, - INPUT_EVENT_MOUSE_BUTTON + INPUT_EVENT_MOUSE_BUTTON, + INPUT_EVENT_MOUSE_WHEEL }; struct KeyPressEventArgs @@ -40,10 +41,12 @@ struct KeyPressEventArgs uint8_t scancode; }; -struct MouseButtonEventArgs +struct MouseUpdateEventArgs { - int button; - int keydown; + unsigned pressed; + unsigned released; + int vdelta; + int hdelta; int xpos; int ypos; }; @@ -77,7 +80,7 @@ public: void extract_video_config(); // windows OSD specific - bool handle_input_event(input_event eventid, void *eventdata) const; + bool handle_input_event(input_event eventid, const void *eventdata) const; bool should_hide_mouse() const; virtual bool has_focus() const override; @@ -89,10 +92,6 @@ public: using osd_common_t::poll_input_modules; // Win32 debugger calls this directly, which it shouldn't -protected: - virtual void build_slider_list() override; - virtual void update_slider_list() override; - private: void process_events(bool ingame, bool nodispatch); virtual void osd_exit() override; |