summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules')
-rw-r--r--src/osd/modules/debugger/debuggdbstub.cpp965
-rw-r--r--src/osd/modules/debugger/debugimgui.cpp295
-rw-r--r--src/osd/modules/debugger/debugqt.cpp2
-rw-r--r--src/osd/modules/debugger/debugwin.cpp13
-rw-r--r--src/osd/modules/debugger/osx/debugview.mm8
-rw-r--r--src/osd/modules/debugger/osx/disassemblyviewer.mm4
-rw-r--r--src/osd/modules/debugger/osx/exceptionpointsview.h23
-rw-r--r--src/osd/modules/debugger/osx/exceptionpointsview.mm27
-rw-r--r--src/osd/modules/debugger/osx/pointsviewer.mm40
-rw-r--r--src/osd/modules/debugger/qt/breakpointswindow.cpp17
-rw-r--r--src/osd/modules/debugger/qt/dasmwindow.cpp1
-rw-r--r--src/osd/modules/debugger/qt/debuggerview.cpp13
-rw-r--r--src/osd/modules/debugger/qt/logwindow.cpp4
-rw-r--r--src/osd/modules/debugger/qt/mainwindow.cpp4
-rw-r--r--src/osd/modules/debugger/qt/memorywindow.cpp5
-rw-r--r--src/osd/modules/debugger/qt/windowqt.cpp4
-rw-r--r--src/osd/modules/debugger/qt/windowqt.h2
-rw-r--r--src/osd/modules/debugger/win/consolewininfo.cpp5
-rw-r--r--src/osd/modules/debugger/win/debugwin.h3
-rw-r--r--src/osd/modules/debugger/win/debugwininfo.cpp7
-rw-r--r--src/osd/modules/debugger/win/debugwininfo.h2
-rw-r--r--src/osd/modules/debugger/win/pointswininfo.cpp21
-rw-r--r--src/osd/modules/debugger/xmlconfig.cpp1
-rw-r--r--src/osd/modules/debugger/xmlconfig.h1
-rw-r--r--src/osd/modules/file/posixfile.cpp14
-rw-r--r--src/osd/modules/font/font_sdl.cpp5
-rw-r--r--src/osd/modules/input/input_common.cpp291
-rw-r--r--src/osd/modules/input/input_common.h161
-rw-r--r--src/osd/modules/input/input_dinput.cpp4
-rw-r--r--src/osd/modules/input/input_rawinput.cpp186
-rw-r--r--src/osd/modules/input/input_sdl.cpp567
-rw-r--r--src/osd/modules/input/input_win32.cpp255
-rw-r--r--src/osd/modules/input/input_wincommon.h5
-rw-r--r--src/osd/modules/input/input_windows.cpp12
-rw-r--r--src/osd/modules/input/input_windows.h2
-rw-r--r--src/osd/modules/lib/osdlib_macosx.cpp17
-rw-r--r--src/osd/modules/lib/osdlib_unix.cpp77
-rw-r--r--src/osd/modules/lib/osdlib_win32.cpp36
-rw-r--r--src/osd/modules/lib/osdobj_common.cpp175
-rw-r--r--src/osd/modules/lib/osdobj_common.h33
-rw-r--r--src/osd/modules/midi/midi_module.h11
-rw-r--r--src/osd/modules/midi/none.cpp27
-rw-r--r--src/osd/modules/midi/portmidi.cpp335
-rw-r--r--src/osd/modules/netdev/netdev_common.cpp39
-rw-r--r--src/osd/modules/netdev/netdev_common.h36
-rw-r--r--src/osd/modules/netdev/netdev_module.h9
-rw-r--r--src/osd/modules/netdev/none.cpp19
-rw-r--r--src/osd/modules/netdev/pcap.cpp223
-rw-r--r--src/osd/modules/netdev/taptun.cpp110
-rw-r--r--src/osd/modules/opengl/gl_shader_tool.h3
-rw-r--r--src/osd/modules/osdwindow.h195
-rw-r--r--src/osd/modules/render/aviwrite.cpp9
-rw-r--r--src/osd/modules/render/bgfx/shadermanager.cpp9
-rw-r--r--src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/fs_xbr-lv3-pass0.sc2
-rw-r--r--src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/fs_xbr-mlv4-pass4.sc2
-rw-r--r--src/osd/modules/render/bgfx/shaders/shader.mk2
-rw-r--r--src/osd/modules/render/d3d/d3dhlsl.cpp2
-rw-r--r--src/osd/modules/render/d3d/d3dhlsl.h4
-rw-r--r--src/osd/modules/render/drawbgfx.cpp48
-rw-r--r--src/osd/modules/render/drawogl.cpp4
-rw-r--r--src/osd/modules/render/drawsdl.cpp16
-rw-r--r--src/osd/modules/sound/coreaudio_sound.cpp77
-rw-r--r--src/osd/modules/sound/direct_sound.cpp42
-rw-r--r--src/osd/modules/sound/js_sound.cpp14
-rw-r--r--src/osd/modules/sound/js_sound.js31
-rw-r--r--src/osd/modules/sound/none.cpp5
-rw-r--r--src/osd/modules/sound/pa_sound.cpp31
-rw-r--r--src/osd/modules/sound/pipewire_sound.cpp823
-rw-r--r--src/osd/modules/sound/pulse_sound.cpp634
-rw-r--r--src/osd/modules/sound/sdl_sound.cpp522
-rw-r--r--src/osd/modules/sound/sound_module.cpp61
-rw-r--r--src/osd/modules/sound/sound_module.h62
-rw-r--r--src/osd/modules/sound/xaudio2_sound.cpp35
73 files changed, 4318 insertions, 2431 deletions
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 &reg: 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 &register_map = it->second;
m_gdb_arch = register_map.arch;
- m_gdb_feature = register_map.feature;
int cur_gdb_regnum = 0;
- for ( const auto &reg: register_map.registers )
- {
- bool added = false;
- for ( const auto &entry: m_state->state_entries() )
+ for ( const auto &feature: register_map.features )
+ for ( const auto &reg: 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 &reg: 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 &reg: 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, &registry_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),
+ &params, 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),
+ &params, 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
//============================================================