diff options
Diffstat (limited to 'src/osd/modules')
362 files changed, 19580 insertions, 11376 deletions
diff --git a/src/osd/modules/debugger/debug_module.h b/src/osd/modules/debugger/debug_module.h index fb659a1b312..1c5b1973c8e 100644 --- a/src/osd/modules/debugger/debug_module.h +++ b/src/osd/modules/debugger/debug_module.h @@ -4,13 +4,15 @@ * debug_module.h * */ +#ifndef MAME_OSD_DEBUGGER_DEBUG_MODULE_H +#define MAME_OSD_DEBUGGER_DEBUG_MODULE_H -#ifndef DEBUG_MODULE_H_ -#define DEBUG_MODULE_H_ +#pragma once #include "osdepend.h" #include "modules/osdmodule.h" + //============================================================ // CONSTANTS //============================================================ @@ -20,14 +22,11 @@ class debug_module { public: - - virtual ~debug_module() { } + virtual ~debug_module() = default; virtual void init_debugger(running_machine &machine) = 0; virtual void wait_for_debugger(device_t &device, bool firststop) = 0; virtual void debugger_update() = 0; }; - - -#endif /* DEBUG_MODULE_H_ */ +#endif // MAME_OSD_DEBUGGER_DEBUG_MODULE_H diff --git a/src/osd/modules/debugger/debuggdbstub.cpp b/src/osd/modules/debugger/debuggdbstub.cpp index eaeb1df4cca..e5092c71548 100644 --- a/src/osd/modules/debugger/debuggdbstub.cpp +++ b/src/osd/modules/debugger/debuggdbstub.cpp @@ -7,17 +7,26 @@ //============================================================ #include "emu.h" +#include "debug_module.h" + #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debug/points.h" #include "debug/textbuf.h" -#include "debug_module.h" #include "debugger.h" -#include "fileio.h" + #include "modules/lib/osdobj_common.h" #include "modules/osdmodule.h" +#include "fileio.h" + #include <cinttypes> +#include <string_view> + + +namespace osd { + +namespace { //------------------------------------------------------------------------- #define MAX_PACKET_SIZE 16384 @@ -245,9 +254,9 @@ static const gdb_register_map gdb_register_map_m68020pmmu = { "A4", "a4", false, TYPE_INT }, { "A5", "a5", false, TYPE_INT }, { "A6", "fp", true, TYPE_INT }, - { "A7", "sp", true, TYPE_INT }, + { "SP", "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 }, + { "CURPC","pc", true, TYPE_CODE_POINTER }, } }; @@ -272,9 +281,9 @@ static const gdb_register_map gdb_register_map_m68000 = { "A4", "a4", false, TYPE_INT }, { "A5", "a5", false, TYPE_INT }, { "A6", "fp", true, TYPE_INT }, - { "A7", "sp", true, TYPE_INT }, + { "SP", "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 }, + { "CURPC","pc", true, TYPE_CODE_POINTER }, //NOTE m68-elf-gdb complains about fpcontrol register not present but 68000 doesn't have floating point so... } }; @@ -401,6 +410,65 @@ 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 }, + } +}; + //------------------------------------------------------------------------- static const std::map<std::string, const gdb_register_map &> gdb_register_maps = { { "i486", gdb_register_map_i486 }, @@ -410,10 +478,17 @@ static const std::map<std::string, const gdb_register_map &> gdb_register_maps = { "m68020pmmu", gdb_register_map_m68020pmmu }, { "m68000", gdb_register_map_m68000 }, { "z80", gdb_register_map_z80 }, + { "z84c015", gdb_register_map_z80 }, { "m6502", gdb_register_map_m6502 }, - { "n2a03", gdb_register_map_m6502 }, + { "m6507", gdb_register_map_m6502 }, + { "m6510", gdb_register_map_m6502 }, + { "m65c02", gdb_register_map_m6502 }, + { "m65ce02", gdb_register_map_m6502 }, + { "rp2a03", gdb_register_map_m6502 }, + { "w65c02s", gdb_register_map_m6502 }, { "m6809", gdb_register_map_m6809 }, { "score7", gdb_register_map_score7 }, + { "nios2", gdb_register_map_nios2 }, }; //------------------------------------------------------------------------- @@ -450,7 +525,7 @@ public: virtual ~debug_gdbstub() { } - virtual int init(const osd_options &options) override; + virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; virtual void init_debugger(running_machine &machine) override; @@ -495,6 +570,7 @@ public: cmd_reply handle_P(const char *buf); cmd_reply handle_q(const char *buf); cmd_reply handle_s(const char *buf); + cmd_reply handle_T(const char *buf); cmd_reply handle_z(const char *buf); cmd_reply handle_Z(const char *buf); @@ -512,7 +588,7 @@ public: int readchar(); - void send_reply(const char *str); + void send_reply(std::string_view str); void send_stop_packet(); private: @@ -563,7 +639,7 @@ private: }; //------------------------------------------------------------------------- -int debug_gdbstub::init(const osd_options &options) +int debug_gdbstub::init(osd_interface &osd, const osd_options &options) { m_debugger_port = options.debugger_port(); return 0; @@ -601,11 +677,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 == '}' ) { @@ -626,10 +702,10 @@ 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); + target_xml += string_format(" <feature name=\"%s\">\n", m_gdb_feature); for ( const auto ®: m_gdb_registers ) - target_xml += string_format(" <reg name=\"%s\" bitsize=\"%d\" type=\"%s\"/>\n", reg.gdb_name.c_str(), reg.gdb_bitsize, gdb_register_type_str[reg.gdb_type]); + target_xml += string_format(" <reg name=\"%s\" bitsize=\"%d\" type=\"%s\"/>\n", reg.gdb_name, reg.gdb_bitsize, gdb_register_type_str[reg.gdb_type]); target_xml += " </feature>\n"; target_xml += "</target>\n"; m_target_xml = escape_packet(target_xml); @@ -644,7 +720,7 @@ void debug_gdbstub::wait_for_debugger(device_t &device, bool firststop) if ( firststop && !m_initialized ) { // find the "main" CPU, which is the first CPU (gdbstub doesn't have any notion of switching CPUs) - m_maincpu = device_type_enumerator<cpu_device>(m_machine->root_device()).first(); + m_maincpu = device_interface_enumerator<cpu_device>(m_machine->root_device()).first(); if (!m_maincpu) fatalerror("gdbstub: cannot find any CPUs\n"); @@ -762,26 +838,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()); } @@ -839,7 +913,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_g(const char *buf) std::string reply; for ( const auto ®: m_gdb_registers ) reply += get_register_string(reg.gdb_regnum); - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } @@ -894,7 +968,8 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_m(const char *buf) return REPLY_ENN; offs_t offset = address; - if ( !m_memory->translate(m_address_space->spacenum(), TRANSLATE_READ_DEBUG, offset) ) + address_space *tspace; + if ( !m_memory->translate(m_address_space->spacenum(), device_memory_interface::TR_READ, offset, tspace) ) return REPLY_ENN; // Disable side effects while reading memory. @@ -904,10 +979,10 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_m(const char *buf) reply.reserve(length * 2); for ( int i = 0; i < length; i++ ) { - uint8_t value = m_address_space->read_byte(offset + i); + uint8_t value = tspace->read_byte(offset + i); reply += string_format("%02x", value); } - send_reply(reply.c_str()); + send_reply(reply); return REPLY_NONE; } @@ -939,7 +1014,8 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_M(const char *buf) return REPLY_ENN; offs_t offset = address; - if ( !m_memory->translate(m_address_space->spacenum(), TRANSLATE_READ_DEBUG, offset) ) + address_space *tspace; + if ( !m_memory->translate(m_address_space->spacenum(), device_memory_interface::TR_READ, offset, tspace) ) return REPLY_ENN; std::vector<uint8_t> data; @@ -947,7 +1023,7 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_M(const char *buf) return REPLY_ENN; for ( int i = 0; i < length; i++ ) - m_address_space->write_byte(offset + i, data[i]); + tspace->write_byte(offset + i, data[i]); return REPLY_OK; } @@ -962,7 +1038,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; } @@ -1029,7 +1105,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; } @@ -1046,7 +1122,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" ) @@ -1067,7 +1143,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; } @@ -1101,6 +1177,17 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_s(const char *buf) } //------------------------------------------------------------------------- +// Find out if the thread XX is alive. +debug_gdbstub::cmd_reply debug_gdbstub::handle_T(const char *buf) +{ + if ( is_thread_id_ok(buf) ) + return REPLY_OK; + + // thread is dead + return REPLY_ENN; +} + +//------------------------------------------------------------------------- static bool remove_breakpoint(device_debug *debug, uint64_t address, int /*kind*/) { const debug_breakpoint *bp = debug->breakpoint_find(address); @@ -1140,9 +1227,10 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_z(const char *buf) // watchpoints offs_t offset = address; + address_space *tspace; if ( type == 2 || type == 3 || type == 4 ) { - if ( !m_memory->translate(m_address_space->spacenum(), TRANSLATE_READ_DEBUG, offset) ) + if ( !m_memory->translate(m_address_space->spacenum(), device_memory_interface::TR_READ, offset, tspace) ) return REPLY_ENN; m_address_map.erase(offset); } @@ -1181,9 +1269,10 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_Z(const char *buf) // watchpoints offs_t offset = address; + address_space *tspace; if ( type == 2 || type == 3 || type == 4 ) { - if ( !m_memory->translate(m_address_space->spacenum(), TRANSLATE_READ_DEBUG, offset) ) + if ( !m_memory->translate(m_address_space->spacenum(), device_memory_interface::TR_READ, offset, tspace) ) return REPLY_ENN; m_address_map[offset] = address; } @@ -1199,15 +1288,15 @@ debug_gdbstub::cmd_reply debug_gdbstub::handle_Z(const char *buf) return REPLY_OK; case 2: // write watchpoint - debug->watchpoint_set(*m_address_space, read_or_write::WRITE, offset, kind, nullptr, nullptr); + debug->watchpoint_set(*m_address_space, read_or_write::WRITE, offset, kind); return REPLY_OK; case 3: // read watchpoint - debug->watchpoint_set(*m_address_space, read_or_write::READ, offset, kind, nullptr, nullptr); + debug->watchpoint_set(*m_address_space, read_or_write::READ, offset, kind); return REPLY_OK; case 4: // access watchpoint - debug->watchpoint_set(*m_address_space, read_or_write::READWRITE, offset, kind, nullptr, nullptr); + debug->watchpoint_set(*m_address_space, read_or_write::READWRITE, offset, kind); return REPLY_OK; } @@ -1241,7 +1330,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); } //------------------------------------------------------------------------- @@ -1270,6 +1359,7 @@ void debug_gdbstub::handle_packet() case 'P': reply = handle_P(buf); break; case 'q': reply = handle_q(buf); break; case 's': reply = handle_s(buf); break; + case 'T': reply = handle_T(buf); break; case 'z': reply = handle_z(buf); break; case 'Z': reply = handle_Z(buf); break; } @@ -1282,22 +1372,6 @@ void debug_gdbstub::handle_packet() } //------------------------------------------------------------------------- -#define BYTESWAP_64(x) ((((x) << 56) & 0xFF00000000000000) \ - | (((x) << 40) & 0x00FF000000000000) \ - | (((x) << 24) & 0x0000FF0000000000) \ - | (((x) << 8) & 0x000000FF00000000) \ - | (((x) >> 8) & 0x00000000FF000000) \ - | (((x) >> 24) & 0x0000000000FF0000) \ - | (((x) >> 40) & 0x000000000000FF00) \ - | (((x) >> 56) & 0x00000000000000FF)) -#define BYTESWAP_32(x) ((((x) << 24) & 0xFF000000) \ - | (((x) << 8) & 0x00FF0000) \ - | (((x) >> 8) & 0x0000FF00) \ - | (((x) >> 24) & 0x000000FF)) -#define BYTESWAP_16(x) ((((x) << 8) & 0xFF00) \ - | (((x) >> 8) & 0x00FF)) - -//------------------------------------------------------------------------- std::string debug_gdbstub::get_register_string(int gdb_regnum) { const gdb_register ® = m_gdb_registers[gdb_regnum]; @@ -1310,9 +1384,9 @@ std::string debug_gdbstub::get_register_string(int gdb_regnum) value &= (1ULL << reg.gdb_bitsize) - 1; if ( !m_is_be ) { - value = (reg.gdb_bitsize == 64) ? BYTESWAP_64(value) - : (reg.gdb_bitsize == 32) ? BYTESWAP_32(value) - : (reg.gdb_bitsize == 16) ? BYTESWAP_16(value) + value = (reg.gdb_bitsize == 64) ? swapendian_int64(value) + : (reg.gdb_bitsize == 32) ? swapendian_int32(value) + : (reg.gdb_bitsize == 16) ? swapendian_int16(value) : value; } return string_format(fmt, value); @@ -1331,9 +1405,9 @@ bool debug_gdbstub::parse_register_string(uint64_t *pvalue, const char *buf, int return false; if ( !m_is_be ) { - value = (reg.gdb_bitsize == 64) ? BYTESWAP_64(value) - : (reg.gdb_bitsize == 32) ? BYTESWAP_32(value) - : (reg.gdb_bitsize == 16) ? BYTESWAP_16(value) + value = (reg.gdb_bitsize == 64) ? swapendian_int64(value) + : (reg.gdb_bitsize == 32) ? swapendian_int32(value) + : (reg.gdb_bitsize == 16) ? swapendian_int16(value) : value; } *pvalue = value; @@ -1430,5 +1504,9 @@ void debug_gdbstub::handle_character(char ch) } } +} // anonymous namespace + +} // namespace osd + //------------------------------------------------------------------------- -MODULE_DEFINITION(DEBUG_GDBSTUB, debug_gdbstub) +MODULE_DEFINITION(DEBUG_GDBSTUB, osd::debug_gdbstub) diff --git a/src/osd/modules/debugger/debugimgui.cpp b/src/osd/modules/debugger/debugimgui.cpp index 944ed203b79..4ae514f857f 100644 --- a/src/osd/modules/debugger/debugimgui.cpp +++ b/src/osd/modules/debugger/debugimgui.cpp @@ -3,9 +3,11 @@ // ImGui based debugger #include "emu.h" +#include "debug_module.h" + #include "imgui/imgui.h" -#include "render.h" -#include "uiinput.h" + +#include "imagedev/floppy.h" #include "debug/debugvw.h" #include "debug/dvdisasm.h" @@ -14,14 +16,20 @@ #include "debug/dvwpoints.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" +#include "debugger.h" +#include "render.h" +#include "uiinput.h" + +#include "formats/flopimg.h" #include "config.h" -#include "debugger.h" #include "modules/lib/osdobj_common.h" -#include "debug_module.h" #include "modules/osdmodule.h" #include "zippath.h" -#include "imagedev/floppy.h" + +namespace osd { + +namespace { class debug_area { @@ -118,7 +126,7 @@ public: virtual ~debug_imgui() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual void exit() override {}; virtual void init_debugger(running_machine &machine) override; @@ -199,6 +207,7 @@ private: file_entry* m_selected_file; int m_format_sel; char m_path[1024]; // path text field buffer + std::unordered_map<input_item_id,ImGuiKey> m_mapping; }; // globals @@ -346,10 +355,11 @@ void debug_imgui::handle_keys() 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))) - io.KeysDown[id] = true; - else - io.KeysDown[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_key_char = 0; @@ -368,9 +378,9 @@ void debug_imgui::handle_keys() } // global keys - if(ImGui::IsKeyPressed(ITEM_ID_F3,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F3,false)) { - if(ImGui::IsKeyDown(ITEM_ID_LSHIFT)) + if(ImGui::IsKeyDown(ImGuiKey_LeftShift)) m_machine->schedule_hard_reset(); else { @@ -379,44 +389,44 @@ void debug_imgui::handle_keys() } } - if(ImGui::IsKeyPressed(ITEM_ID_F5,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F5,false)) { m_machine->debugger().console().get_visible_cpu()->debug()->go(); m_running = true; } - if(ImGui::IsKeyPressed(ITEM_ID_F6,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F6,false)) { m_machine->debugger().console().get_visible_cpu()->debug()->go_next_device(); m_running = true; } - if(ImGui::IsKeyPressed(ITEM_ID_F7,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F7,false)) { m_machine->debugger().console().get_visible_cpu()->debug()->go_interrupt(); m_running = true; } - if(ImGui::IsKeyPressed(ITEM_ID_F8,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F8,false)) m_machine->debugger().console().get_visible_cpu()->debug()->go_vblank(); - if(ImGui::IsKeyPressed(ITEM_ID_F9,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F9,false)) m_machine->debugger().console().get_visible_cpu()->debug()->single_step_out(); - if(ImGui::IsKeyPressed(ITEM_ID_F10,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F10,false)) m_machine->debugger().console().get_visible_cpu()->debug()->single_step_over(); - if(ImGui::IsKeyPressed(ITEM_ID_F11,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F11,false)) m_machine->debugger().console().get_visible_cpu()->debug()->single_step(); - if(ImGui::IsKeyPressed(ITEM_ID_F12,false)) + if(ImGui::IsKeyPressed(ImGuiKey_F12,false)) { m_machine->debugger().console().get_visible_cpu()->debug()->go(); m_hide = true; } - if(ImGui::IsKeyPressed(ITEM_ID_D,false) && ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyPressed(ImGuiKey_D,false) && io.KeyCtrl) add_disasm(++m_win_count); - if(ImGui::IsKeyPressed(ITEM_ID_M,false) && ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyPressed(ImGuiKey_M,false) && io.KeyCtrl) add_memory(++m_win_count); - if(ImGui::IsKeyPressed(ITEM_ID_B,false) && ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyPressed(ImGuiKey_B,false) && io.KeyCtrl) add_bpoints(++m_win_count); - if(ImGui::IsKeyPressed(ITEM_ID_W,false) && ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyPressed(ImGuiKey_W,false) && io.KeyCtrl) add_wpoints(++m_win_count); - if(ImGui::IsKeyPressed(ITEM_ID_L,false) && ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyPressed(ImGuiKey_L,false) && io.KeyCtrl) add_log(++m_win_count); } @@ -439,38 +449,38 @@ void debug_imgui::handle_keys_views() return; // pass keypresses to debug view with focus - if(ImGui::IsKeyPressed(ITEM_ID_UP)) + if(ImGui::IsKeyPressed(ImGuiKey_UpArrow)) focus_view->view->process_char(DCH_UP); - if(ImGui::IsKeyPressed(ITEM_ID_DOWN)) + if(ImGui::IsKeyPressed(ImGuiKey_DownArrow)) focus_view->view->process_char(DCH_DOWN); - if(ImGui::IsKeyPressed(ITEM_ID_LEFT)) + if(ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) { - if(ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) focus_view->view->process_char(DCH_CTRLLEFT); else focus_view->view->process_char(DCH_LEFT); } - if(ImGui::IsKeyPressed(ITEM_ID_RIGHT)) + if(ImGui::IsKeyPressed(ImGuiKey_RightArrow)) { - if(ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) focus_view->view->process_char(DCH_CTRLRIGHT); else focus_view->view->process_char(DCH_RIGHT); } - if(ImGui::IsKeyPressed(ITEM_ID_PGUP)) + if(ImGui::IsKeyPressed(ImGuiKey_PageUp)) focus_view->view->process_char(DCH_PUP); - if(ImGui::IsKeyPressed(ITEM_ID_PGDN)) + if(ImGui::IsKeyPressed(ImGuiKey_PageDown)) focus_view->view->process_char(DCH_PDOWN); - if(ImGui::IsKeyPressed(ITEM_ID_HOME)) + if(ImGui::IsKeyPressed(ImGuiKey_Home)) { - if(ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) focus_view->view->process_char(DCH_CTRLHOME); else focus_view->view->process_char(DCH_HOME); } - if(ImGui::IsKeyPressed(ITEM_ID_END)) + if(ImGui::IsKeyPressed(ImGuiKey_End)) { - if(ImGui::IsKeyDown(ITEM_ID_LCONTROL)) + if(ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) focus_view->view->process_char(DCH_CTRLEND); else focus_view->view->process_char(DCH_END); @@ -547,6 +557,8 @@ int debug_imgui::history_set(ImGuiInputTextCallbackData* data) if(history_pos < view_main_console->console_history.size()) history_pos++; break; + default: + break; } if(history_pos == view_main_console->console_history.size()) @@ -606,7 +618,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; @@ -985,18 +1000,18 @@ void debug_imgui::mount_image() void debug_imgui::create_image() { - image_init_result res; + std::pair<std::error_condition, std::string> res; auto *fd = dynamic_cast<floppy_image_device *>(m_dialog_image); if(fd != nullptr) { res = fd->create(m_path,nullptr,nullptr); - if(res == image_init_result::PASS) + if(!res.first) fd->setup_write(m_typelist.at(m_format_sel).format); } else res = m_dialog_image->create(m_path,nullptr,nullptr); - if(res == image_init_result::PASS) + if(!res.first) ImGui::CloseCurrentPopup(); // TODO: add a messagebox to display on an error } @@ -1013,7 +1028,6 @@ void debug_imgui::refresh_filelist() std::error_condition const err = util::zippath_directory::open(m_path,dir); if(!err) { - int x = 0; // add drives for(std::string const &volume_name : osd_get_volume_names()) { @@ -1022,19 +1036,18 @@ void debug_imgui::refresh_filelist() temp.basename = volume_name; temp.fullpath = volume_name; m_filelist.emplace_back(std::move(temp)); - x++; } first = m_filelist.size(); - const osd::directory::entry *dirent; + const directory::entry *dirent; while((dirent = dir->readdir()) != nullptr) { file_entry temp; switch(dirent->type) { - case osd::directory::entry::entry_type::FILE: + case directory::entry::entry_type::FILE: temp.type = file_entry_type::FILE; break; - case osd::directory::entry::entry_type::DIR: + case directory::entry::entry_type::DIR: temp.type = file_entry_type::DIRECTORY; break; default: @@ -1190,10 +1203,10 @@ void debug_imgui::draw_create_dialog(const char* label) if(ImGui::InputText("##createfilename",m_path,1024,ImGuiInputTextFlags_EnterReturnsTrue)) { auto entry = osd_stat(m_path); - auto file_type = (entry != nullptr) ? entry->type : osd::directory::entry::entry_type::NONE; - if(file_type == osd::directory::entry::entry_type::NONE) + auto file_type = (entry != nullptr) ? entry->type : directory::entry::entry_type::NONE; + if(file_type == directory::entry::entry_type::NONE) create_image(); - if(file_type == osd::directory::entry::entry_type::FILE) + if(file_type == directory::entry::entry_type::FILE) m_create_confirm_wait = true; // cannot overwrite a directory, so nothing will be none in that case. } @@ -1240,10 +1253,10 @@ void debug_imgui::draw_create_dialog(const char* label) if(ImGui::Button("OK##mount")) { auto entry = osd_stat(m_path); - auto file_type = (entry != nullptr) ? entry->type : osd::directory::entry::entry_type::NONE; - if(file_type == osd::directory::entry::entry_type::NONE) + auto file_type = (entry != nullptr) ? entry->type : directory::entry::entry_type::NONE; + if(file_type == directory::entry::entry_type::NONE) create_image(); - if(file_type == osd::directory::entry::entry_type::FILE) + if(file_type == directory::entry::entry_type::FILE) m_create_confirm_wait = true; // cannot overwrite a directory, so nothing will be none in that case. m_create_open = false; @@ -1393,7 +1406,6 @@ void debug_imgui::update() //debug_area* view_ptr = view_list; std::vector<debug_area*>::iterator view_ptr; bool opened; - int count = 0; ImGui::PushStyleColor(ImGuiCol_WindowBg,ImVec4(1.0f,1.0f,1.0f,0.9f)); ImGui::PushStyleColor(ImGuiCol_Text,ImVec4(0.0f,0.0f,0.0f,1.0f)); ImGui::PushStyleColor(ImGuiCol_TextDisabled,ImVec4(0.0f,0.0f,1.0f,1.0f)); @@ -1438,7 +1450,6 @@ void debug_imgui::update() break; } ++view_ptr; - count++; } // check for a closed window if(to_delete != nullptr) @@ -1464,26 +1475,39 @@ void debug_imgui::init_debugger(running_machine &machine) m_has_images = true; // map keys to ImGui inputs - io.KeyMap[ImGuiKey_A] = ITEM_ID_A; - io.KeyMap[ImGuiKey_C] = ITEM_ID_C; - io.KeyMap[ImGuiKey_V] = ITEM_ID_V; - io.KeyMap[ImGuiKey_X] = ITEM_ID_X; - io.KeyMap[ImGuiKey_C] = ITEM_ID_C; - io.KeyMap[ImGuiKey_Y] = ITEM_ID_Y; - io.KeyMap[ImGuiKey_Z] = ITEM_ID_Z; - io.KeyMap[ImGuiKey_Backspace] = ITEM_ID_BACKSPACE; - io.KeyMap[ImGuiKey_Delete] = ITEM_ID_DEL; - io.KeyMap[ImGuiKey_Tab] = ITEM_ID_TAB; - io.KeyMap[ImGuiKey_PageUp] = ITEM_ID_PGUP; - io.KeyMap[ImGuiKey_PageDown] = ITEM_ID_PGDN; - io.KeyMap[ImGuiKey_Home] = ITEM_ID_HOME; - io.KeyMap[ImGuiKey_End] = ITEM_ID_END; - io.KeyMap[ImGuiKey_Escape] = ITEM_ID_ESC; - io.KeyMap[ImGuiKey_Enter] = ITEM_ID_ENTER; - io.KeyMap[ImGuiKey_LeftArrow] = ITEM_ID_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = ITEM_ID_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = ITEM_ID_UP; - io.KeyMap[ImGuiKey_DownArrow] = ITEM_ID_DOWN; + m_mapping[ITEM_ID_A] = ImGuiKey_A; + m_mapping[ITEM_ID_C] = ImGuiKey_C; + m_mapping[ITEM_ID_V] = ImGuiKey_V; + m_mapping[ITEM_ID_X] = ImGuiKey_X; + m_mapping[ITEM_ID_Y] = ImGuiKey_Y; + m_mapping[ITEM_ID_Z] = ImGuiKey_Z; + m_mapping[ITEM_ID_D] = ImGuiKey_D; + m_mapping[ITEM_ID_M] = ImGuiKey_M; + m_mapping[ITEM_ID_B] = ImGuiKey_B; + m_mapping[ITEM_ID_W] = ImGuiKey_W; + m_mapping[ITEM_ID_L] = ImGuiKey_L; + m_mapping[ITEM_ID_BACKSPACE] = ImGuiKey_Backspace; + m_mapping[ITEM_ID_DEL] = ImGuiKey_Delete; + m_mapping[ITEM_ID_TAB] = ImGuiKey_Tab; + m_mapping[ITEM_ID_PGUP] = ImGuiKey_PageUp; + m_mapping[ITEM_ID_PGDN] = ImGuiKey_PageDown; + m_mapping[ITEM_ID_HOME] = ImGuiKey_Home; + m_mapping[ITEM_ID_END] = ImGuiKey_End; + m_mapping[ITEM_ID_ESC] = ImGuiKey_Escape; + m_mapping[ITEM_ID_ENTER] = ImGuiKey_Enter; + m_mapping[ITEM_ID_LEFT] = ImGuiKey_LeftArrow; + m_mapping[ITEM_ID_RIGHT] = ImGuiKey_RightArrow; + m_mapping[ITEM_ID_UP] = ImGuiKey_UpArrow; + m_mapping[ITEM_ID_DOWN] = ImGuiKey_DownArrow; + m_mapping[ITEM_ID_F3] = ImGuiKey_F3; + m_mapping[ITEM_ID_F5] = ImGuiKey_F5; + m_mapping[ITEM_ID_F6] = ImGuiKey_F6; + m_mapping[ITEM_ID_F7] = ImGuiKey_F7; + m_mapping[ITEM_ID_F8] = ImGuiKey_F8; + m_mapping[ITEM_ID_F9] = ImGuiKey_F9; + m_mapping[ITEM_ID_F10] = ImGuiKey_F10; + m_mapping[ITEM_ID_F11] = ImGuiKey_F11; + m_mapping[ITEM_ID_F12] = ImGuiKey_F12; // set key delay and repeat rates io.KeyRepeatDelay = 0.400f; @@ -1534,7 +1558,7 @@ void debug_imgui::wait_for_debugger(device_t &device, bool firststop) m_running = false; } m_hide = false; - //m_machine->ui_input().frame_update(); + m_machine->osd().input_update(true); handle_mouse(); handle_keys(); handle_console(m_machine); @@ -1559,6 +1583,7 @@ void debug_imgui::debugger_update() { uint32_t width = m_machine->render().ui_target().width(); uint32_t height = m_machine->render().ui_target().height(); + m_machine->osd().input_update(true); handle_mouse(); handle_keys(); imguiBeginFrame(m_mouse_x,m_mouse_y,m_mouse_button ? IMGUI_MBUT_LEFT : 0, 0, width, height, m_key_char); @@ -1567,4 +1592,8 @@ void debug_imgui::debugger_update() } } -MODULE_DEFINITION(DEBUG_IMGUI, debug_imgui) +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(DEBUG_IMGUI, osd::debug_imgui) diff --git a/src/osd/modules/debugger/debugosx.mm b/src/osd/modules/debugger/debugosx.mm index 66797e38453..a4721e4d292 100644 --- a/src/osd/modules/debugger/debugosx.mm +++ b/src/osd/modules/debugger/debugosx.mm @@ -24,11 +24,6 @@ // MAMEOS headers #include "modules/lib/osdobj_common.h" #include "osx/debugosx.h" -#ifdef OSD_MAC -#include "osdmac.h" -#else -#include "osdsdl.h" -#endif #include "debug_module.h" #import "osx/debugconsole.h" @@ -61,12 +56,12 @@ public: [m_console release]; } - virtual int init(const osd_options &options); - virtual void exit(); + virtual int init(osd_interface &osd, const osd_options &options) override; + virtual void exit() override; - virtual void init_debugger(running_machine &machine); - virtual void wait_for_debugger(device_t &device, bool firststop); - virtual void debugger_update(); + virtual void init_debugger(running_machine &machine) override; + virtual void wait_for_debugger(device_t &device, bool firststop) override; + virtual void debugger_update() override; private: void create_console(); @@ -91,7 +86,7 @@ std::atomic_bool debugger_osx::s_added_menus(false); // initialise debugger module //============================================================ -int debugger_osx::init(const osd_options &options) +int debugger_osx::init(osd_interface &osd, const osd_options &options) { return 0; } @@ -165,7 +160,7 @@ void debugger_osx::wait_for_debugger(device_t &device, bool firststop) } // get and process messages - NSEvent *ev = [NSApp nextEventMatchingMask:NSAnyEventMask + NSEvent *ev = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; @@ -241,7 +236,7 @@ void debugger_osx::build_menus() [[debugMenu addItemWithTitle:@"Hard Reset" action:@selector(debugHardReset:) keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] - setKeyEquivalentModifierMask:NSShiftKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagShift]; NSMenu *const runMenu = [[NSMenu alloc] initWithTitle:@"Run"]; item = [[NSApp mainMenu] insertItemWithTitle:@"Run" @@ -295,7 +290,7 @@ void debugger_osx::build_menus() [[runMenu addItemWithTitle:@"Step Out" action:@selector(debugStepOut:) keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] - setKeyEquivalentModifierMask:NSShiftKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagShift]; } } diff --git a/src/osd/modules/debugger/debugqt.cpp b/src/osd/modules/debugger/debugqt.cpp index a48f419a6a3..cdf3fb014bc 100644 --- a/src/osd/modules/debugger/debugqt.cpp +++ b/src/osd/modules/debugger/debugqt.cpp @@ -32,34 +32,64 @@ #include "qt/deviceswindow.h" #include "qt/deviceinformationwindow.h" -class debug_qt : public osd_module, public debug_module +#include "util/xmlfile.h" + + +#if defined(SDLMAME_UNIX) || defined(SDLMAME_WIN32) +extern int sdl_entered_debugger; +#elif defined(_WIN32) +void winwindow_update_cursor_state(running_machine &machine); +bool winwindow_qt_filter(void *message); +#endif + + +namespace osd { + +namespace { + +class debug_qt : public osd_module, public debug_module, protected debugger::qt::DebuggerQt #if defined(_WIN32) && !defined(SDLMAME_WIN32) , public QAbstractNativeEventFilter #endif { public: - debug_qt() : osd_module(OSD_DEBUG_PROVIDER, "qt"), debug_module(), m_machine(nullptr) + debug_qt() : + osd_module(OSD_DEBUG_PROVIDER, "qt"), + debug_module(), + m_machine(nullptr), + m_mainwindow(nullptr) { } virtual ~debug_qt() { } - virtual int init(const osd_options &options) { return 0; } - virtual void exit() { } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } + virtual void exit() override; - virtual void init_debugger(running_machine &machine); - virtual void wait_for_debugger(device_t &device, bool firststop); - virtual void debugger_update(); + virtual void init_debugger(running_machine &machine) override; + virtual void wait_for_debugger(device_t &device, bool firststop) override; + virtual void debugger_update() override; #if defined(_WIN32) && !defined(SDLMAME_WIN32) - virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE; + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override + { + winwindow_qt_filter(message); + return false; + } #endif + + virtual running_machine &machine() const override { return *m_machine; } + private: + void configuration_load(config_type which_type, config_level level, util::xml::data_node const *parentnode); + void configuration_save(config_type which_type, util::xml::data_node *parentnode); + void load_window_configurations(util::xml::data_node const &parentnode); + running_machine *m_machine; + debugger::qt::MainWindow *m_mainwindow; + util::xml::file::ptr m_config; }; -namespace { - //============================================================ // "Global" variables to make QT happy //============================================================ @@ -68,177 +98,18 @@ int qtArgc = 1; char qtArg0[] = "mame"; char *qtArgv[] = { qtArg0, nullptr }; -bool oneShot = true; -MainWindow *mainQtWindow = nullptr; - -//============================================================ -// XML configuration save/load -//============================================================ - -// Global variable used to feed the xml configuration callbacks -std::vector<std::unique_ptr<WindowQtConfig> > xmlConfigurations; - - -void xml_configuration_load(running_machine &machine, config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) -{ - // We only care about system configuration files - if ((cfg_type != config_type::SYSTEM) || !parentnode) - return; - - xmlConfigurations.clear(); - - // Configuration load - util::xml::data_node const *wnode = nullptr; - for (wnode = parentnode->get_child("window"); wnode; wnode = wnode->get_next_sibling("window")) - { - WindowQtConfig::WindowType type = (WindowQtConfig::WindowType)wnode->get_attribute_int("type", WindowQtConfig::WIN_TYPE_UNKNOWN); - switch (type) - { - case WindowQtConfig::WIN_TYPE_MAIN: xmlConfigurations.push_back(std::make_unique<MainWindowQtConfig>()); break; - case WindowQtConfig::WIN_TYPE_MEMORY: xmlConfigurations.push_back(std::make_unique<MemoryWindowQtConfig>()); break; - case WindowQtConfig::WIN_TYPE_DASM: xmlConfigurations.push_back(std::make_unique<DasmWindowQtConfig>()); break; - case WindowQtConfig::WIN_TYPE_LOG: xmlConfigurations.push_back(std::make_unique<LogWindowQtConfig>()); break; - case WindowQtConfig::WIN_TYPE_BREAK_POINTS: xmlConfigurations.push_back(std::make_unique<BreakpointsWindowQtConfig>()); break; - case WindowQtConfig::WIN_TYPE_DEVICES: xmlConfigurations.push_back(std::make_unique<DevicesWindowQtConfig>()); break; - case WindowQtConfig::WIN_TYPE_DEVICE_INFORMATION: xmlConfigurations.push_back(std::make_unique<DeviceInformationWindowQtConfig>()); break; - default: continue; - } - xmlConfigurations.back()->recoverFromXmlNode(*wnode); - } -} - - -void xml_configuration_save(running_machine &machine, config_type cfg_type, util::xml::data_node *parentnode) -{ - // We only save system configuration - if (cfg_type != config_type::SYSTEM) - return; - - for (int i = 0; i < xmlConfigurations.size(); i++) - { - WindowQtConfig &config = *xmlConfigurations[i]; - - // Create an xml node - util::xml::data_node *const debugger_node = parentnode->add_child("window", nullptr); - - // Insert the appropriate information - if (debugger_node) - config.addToXmlDataNode(*debugger_node); - } -} - - -void gather_save_configurations() -{ - xmlConfigurations.clear(); - - // Loop over all the open windows - foreach (QWidget *widget, QApplication::topLevelWidgets()) - { - if (!widget->isVisible()) - continue; - - if (!widget->isWindow() || widget->windowType() != Qt::Window) - continue; - - // Figure out its type - if (dynamic_cast<MainWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<MainWindowQtConfig>()); - else if (dynamic_cast<MemoryWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<MemoryWindowQtConfig>()); - else if (dynamic_cast<DasmWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<DasmWindowQtConfig>()); - else if (dynamic_cast<LogWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<LogWindowQtConfig>()); - else if (dynamic_cast<BreakpointsWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<BreakpointsWindowQtConfig>()); - else if (dynamic_cast<DevicesWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<DevicesWindowQtConfig>()); - else if (dynamic_cast<DeviceInformationWindow *>(widget)) - xmlConfigurations.push_back(std::make_unique<DeviceInformationWindowQtConfig>()); - - xmlConfigurations.back()->buildFromQWidget(widget); - } -} - - -//============================================================ -// Utilities -//============================================================ - -void load_and_clear_main_window_config(std::vector<std::unique_ptr<WindowQtConfig> > &configList) -{ - for (int i = 0; i < configList.size(); i++) - { - WindowQtConfig &config = *configList[i]; - if (config.m_type == WindowQtConfig::WIN_TYPE_MAIN) - { - config.applyToQWidget(mainQtWindow); - configList.erase(configList.begin() + i); - break; - } - } -} - - -void setup_additional_startup_windows(running_machine &machine, std::vector<std::unique_ptr<WindowQtConfig> > &configList) -{ - for (int i = 0; i < configList.size(); i++) - { - WindowQtConfig &config = *configList[i]; - - WindowQt *foo = nullptr; - switch (config.m_type) - { - case WindowQtConfig::WIN_TYPE_MEMORY: - foo = new MemoryWindow(machine); break; - case WindowQtConfig::WIN_TYPE_DASM: - foo = new DasmWindow(machine); break; - case WindowQtConfig::WIN_TYPE_LOG: - foo = new LogWindow(machine); break; - case WindowQtConfig::WIN_TYPE_BREAK_POINTS: - foo = new BreakpointsWindow(machine); break; - case WindowQtConfig::WIN_TYPE_DEVICES: - foo = new DevicesWindow(machine); break; - case WindowQtConfig::WIN_TYPE_DEVICE_INFORMATION: - foo = new DeviceInformationWindow(machine); break; - default: - break; - } - config.applyToQWidget(foo); - foo->show(); - } -} - - -void bring_main_window_to_front() -{ - foreach (QWidget *widget, QApplication::topLevelWidgets()) - { - if (dynamic_cast<MainWindow *>(widget)) - { - widget->activateWindow(); - widget->raise(); - } - } -} - -} // anonymous namespace - //============================================================ // Core functionality //============================================================ -#if defined(_WIN32) && !defined(SDLMAME_WIN32) -bool winwindow_qt_filter(void *message); - -bool debug_qt::nativeEventFilter(const QByteArray &eventType, void *message, long *) +void debug_qt::exit() { - winwindow_qt_filter(message); - return false; + // If you've done a hard reset, clear out existing widgets + emit exitDebugger(); + m_mainwindow = nullptr; } -#endif + void debug_qt::init_debugger(running_machine &machine) { @@ -250,23 +121,13 @@ void debug_qt::init_debugger(running_machine &machine) QAbstractEventDispatcher::instance()->installNativeEventFilter(this); #endif } - else - { - // If you've done a hard reset, clear out existing widgets & get ready for re-init - foreach (QWidget *widget, QApplication::topLevelWidgets()) - { - if (!widget->isWindow() || widget->windowType() != Qt::Window) - continue; - delete widget; - } - oneShot = true; - } m_machine = &machine; + // Setup the configuration XML saving and loading machine.configuration().config_register("debugger", - configuration_manager::load_delegate(&xml_configuration_load, &machine), - configuration_manager::save_delegate(&xml_configuration_save, &machine)); + configuration_manager::load_delegate(&debug_qt::configuration_load, this), + configuration_manager::save_delegate(&debug_qt::configuration_save, this)); } @@ -274,12 +135,6 @@ void debug_qt::init_debugger(running_machine &machine) // Core functionality //============================================================ -#if defined(SDLMAME_UNIX) || defined(SDLMAME_WIN32) -extern int sdl_entered_debugger; -#elif defined(_WIN32) -void winwindow_update_cursor_state(running_machine &machine); -#endif - void debug_qt::wait_for_debugger(device_t &device, bool firststop) { #if defined(SDLMAME_UNIX) || defined(SDLMAME_WIN32) @@ -287,62 +142,33 @@ void debug_qt::wait_for_debugger(device_t &device, bool firststop) #endif // Dialog initialization - if (oneShot) + if (!m_mainwindow) { - mainQtWindow = new MainWindow(*m_machine); - load_and_clear_main_window_config(xmlConfigurations); - setup_additional_startup_windows(*m_machine, xmlConfigurations); - mainQtWindow->show(); - oneShot = false; + m_mainwindow = new debugger::qt::MainWindow(*this); + if (m_config) + { + load_window_configurations(*m_config->get_first_child()); + m_config.reset(); + } + m_mainwindow->show(); } // Ensure all top level widgets are visible & bring main window to front - foreach (QWidget *widget, QApplication::topLevelWidgets()) - { - if (!widget->isWindow() || widget->windowType() != Qt::Window) - continue; - widget->show(); - } + emit showAllWindows(); if (firststop) - bring_main_window_to_front(); + { + m_mainwindow->activateWindow(); + m_mainwindow->raise(); + } - // Set the main window to display the proper cpu - mainQtWindow->setProcessor(&device); + // Set the main window to display the proper CPU + m_mainwindow->setProcessor(&device); // Run our own QT event loop osd_sleep(osd_ticks_per_second() / 1000 * 50); qApp->processEvents(QEventLoop::AllEvents, 1); - // Refresh everyone if requested - if (mainQtWindow->wantsRefresh()) - { - QWidgetList allWidgets = qApp->allWidgets(); - for (int i = 0; i < allWidgets.length(); i++) - allWidgets[i]->update(); - mainQtWindow->clearRefreshFlag(); - } - - // Hide all top level widgets if requested - if (mainQtWindow->wantsHide()) - { - foreach (QWidget *widget, QApplication::topLevelWidgets()) - { - if (!widget->isWindow() || widget->windowType() != Qt::Window) - continue; - widget->hide(); - } - mainQtWindow->clearHideFlag(); - } - - // Exit if the machine has been instructed to do so (scheduled event == exit || hard_reset) - if (m_machine->scheduled_event_pending()) - { - // Keep a list of windows we want to save. - // We need to do this here because by the time xml_configuration_save gets called - // all the QT windows are already gone. - gather_save_configurations(); - } #if defined(_WIN32) && !defined(SDLMAME_WIN32) winwindow_update_cursor_state(*m_machine); // make sure the cursor isn't hidden while in debugger #endif @@ -358,10 +184,75 @@ void debug_qt::debugger_update() qApp->processEvents(QEventLoop::AllEvents, 1); } + +void debug_qt::configuration_load(config_type which_type, config_level level, util::xml::data_node const *parentnode) +{ + // We only care about system configuration files for now + if ((config_type::SYSTEM == which_type) && parentnode) + { + if (m_mainwindow) + { + load_window_configurations(*parentnode); + } + else + { + m_config = util::xml::file::create(); + parentnode->copy_into(*m_config); + } + } +} + + +void debug_qt::configuration_save(config_type which_type, util::xml::data_node *parentnode) +{ + // We only save system configuration for now + if ((config_type::SYSTEM == which_type) && parentnode) + emit saveConfiguration(*parentnode); +} + + +void debug_qt::load_window_configurations(util::xml::data_node const &parentnode) +{ + for (util::xml::data_node const *wnode = parentnode.get_child(debugger::NODE_WINDOW); wnode; wnode = wnode->get_next_sibling(debugger::NODE_WINDOW)) + { + debugger::qt::WindowQt *win = nullptr; + switch (wnode->get_attribute_int(debugger::ATTR_WINDOW_TYPE, -1)) + { + case debugger::WINDOW_TYPE_CONSOLE: + win = m_mainwindow; + break; + case debugger::WINDOW_TYPE_MEMORY_VIEWER: + win = new debugger::qt::MemoryWindow(*this); + break; + case debugger::WINDOW_TYPE_DISASSEMBLY_VIEWER: + win = new debugger::qt::DasmWindow(*this); + break; + case debugger::WINDOW_TYPE_ERROR_LOG_VIEWER: + win = new debugger::qt::LogWindow(*this); + break; + case debugger::WINDOW_TYPE_POINTS_VIEWER: + win = new debugger::qt::BreakpointsWindow(*this); + break; + case debugger::WINDOW_TYPE_DEVICES_VIEWER: + win = new debugger::qt::DevicesWindow(*this); + break; + case debugger::WINDOW_TYPE_DEVICE_INFO_VIEWER: + win = new debugger::qt::DeviceInformationWindow(*this); + break; + } + if (win) + win->restoreConfiguration(*wnode); + } +} + +} // anonymous namespace + +} // namespace osd + #else // USE_QTDEBUG -MODULE_NOT_SUPPORTED(debug_qt, OSD_DEBUG_PROVIDER, "qt") +namespace osd { namespace { MODULE_NOT_SUPPORTED(debug_qt, OSD_DEBUG_PROVIDER, "qt") } } #endif -MODULE_DEFINITION(DEBUG_QT, debug_qt) +MODULE_DEFINITION(DEBUG_QT, osd::debug_qt) diff --git a/src/osd/modules/debugger/debugwin.cpp b/src/osd/modules/debugger/debugwin.cpp index 18bbc07dd70..43d20ae371d 100644 --- a/src/osd/modules/debugger/debugwin.cpp +++ b/src/osd/modules/debugger/debugwin.cpp @@ -2,13 +2,12 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// debugwin.c - Win32 debug window handling +// debugwin.cpp - Win32 debug window handling // //============================================================ #include "emu.h" #include "debug_module.h" -#include "modules/osdmodule.h" #if defined(OSD_WINDOWS) /*|| defined(SDLMAME_WIN32)*/ @@ -22,31 +21,48 @@ #include "win/pointswininfo.h" #include "win/uimetrics.h" +// emu +#include "config.h" #include "debugger.h" #include "debug/debugcpu.h" +#include "util/xmlfile.h" + +// osd/windows #include "window.h" -#include "../input/input_common.h" -#include "../input/input_windows.h" +#include "winmain.h" + +#include "../input/input_windows.h" // for the keyboard translation table + +namespace osd { -class debugger_windows : public osd_module, public debug_module, protected debugger_windows_interface +namespace { + +class debugger_windows : + public osd_module, + public debug_module, + protected debugger::win::debugger_windows_interface { public: debugger_windows() : osd_module(OSD_DEBUG_PROVIDER, "windows"), debug_module(), + m_osd(nullptr), m_machine(nullptr), m_metrics(), m_waiting_for_debugger(false), m_window_list(), - m_main_console(nullptr) + m_main_console(nullptr), + m_next_window_pos{ 0, 0 }, + m_config(), + m_save_windows(true) { } virtual ~debugger_windows() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, osd_options const &options) override; virtual void exit() override; virtual void init_debugger(running_machine &machine) override; @@ -56,31 +72,58 @@ public: protected: virtual running_machine &machine() const override { return *m_machine; } - virtual ui_metrics &metrics() const override { return *m_metrics; } + virtual debugger::win::ui_metrics &metrics() const override { return *m_metrics; } + 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 const &waiting_for_debugger() const override { return m_waiting_for_debugger; } virtual bool seq_pressed() const override; - virtual void create_memory_window() override { create_window<memorywin_info>(); } - virtual void create_disasm_window() override { create_window<disasmwin_info>(); } - virtual void create_log_window() override { create_window<logwin_info>(); } - virtual void create_points_window() override { create_window<pointswin_info>(); } - virtual void remove_window(debugwin_info &info) override; + virtual void create_memory_window() override { create_window<debugger::win::memorywin_info>(); } + virtual void create_disasm_window() override { create_window<debugger::win::disasmwin_info>(); } + virtual void create_log_window() override { create_window<debugger::win::logwin_info>(); } + virtual void create_points_window() override { create_window<debugger::win::pointswin_info>(); } + virtual void remove_window(debugger::win::debugwin_info &info) override; virtual void show_all() override; virtual void hide_all() override; + virtual void stagger_window(HWND window, int width, int height) override; + private: template <typename T> T *create_window(); - running_machine *m_machine; - std::unique_ptr<ui_metrics> m_metrics; - bool m_waiting_for_debugger; - std::vector<std::unique_ptr<debugwin_info>> m_window_list; - consolewin_info *m_main_console; + void config_load(config_type cfgtype, config_level cfglevel, util::xml::data_node const *parentnode); + void config_save(config_type cfgtype, util::xml::data_node *parentnode); + + void load_configuration(util::xml::data_node const &parentnode); + + windows_osd_interface *m_osd; + running_machine *m_machine; + std::unique_ptr<debugger::win::ui_metrics> m_metrics; + bool m_waiting_for_debugger; + std::vector<std::unique_ptr<debugger::win::debugwin_info> > m_window_list; + debugger::win::consolewin_info *m_main_console; + + POINT m_next_window_pos; + LONG m_window_start_x; + + util::xml::file::ptr m_config; + bool m_save_windows; }; +int debugger_windows::init(osd_interface &osd, osd_options const &options) +{ + m_osd = dynamic_cast<windows_osd_interface *>(&osd); + if (!m_osd) + return -1; + + return 0; +} + + void debugger_windows::exit() { // loop over windows and free them @@ -96,23 +139,52 @@ void debugger_windows::exit() void debugger_windows::init_debugger(running_machine &machine) { m_machine = &machine; - m_metrics = std::make_unique<ui_metrics>(downcast<osd_options &>(m_machine->options())); + m_metrics = std::make_unique<debugger::win::ui_metrics>(downcast<osd_options &>(m_machine->options())); + machine.configuration().config_register( + "debugger", + configuration_manager::load_delegate(&debugger_windows::config_load, this), + configuration_manager::save_delegate(&debugger_windows::config_save, this)); } void debugger_windows::wait_for_debugger(device_t &device, bool firststop) { // create a console window - if (m_main_console == nullptr) - m_main_console = create_window<consolewin_info>(); + if (!m_main_console) + { + m_main_console = create_window<debugger::win::consolewin_info>(); + + // set the starting position for new auxiliary windows + HMONITOR const nearest_monitor = MonitorFromWindow( + dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(), + MONITOR_DEFAULTTONEAREST); + if (nearest_monitor) + { + MONITORINFO info; + std::memset(&info, 0, sizeof(info)); + info.cbSize = sizeof(info); + if (GetMonitorInfo(nearest_monitor, &info)) + { + m_next_window_pos.x = info.rcWork.left + 100; + m_next_window_pos.y = info.rcWork.top + 100; + m_window_start_x = m_next_window_pos.x; + } + } + } // update the views in the console to reflect the current CPU - if (m_main_console != nullptr) + if (m_main_console) m_main_console->set_cpu(device); // when we are first stopped, adjust focus to us - if (firststop && (m_main_console != nullptr)) + if (firststop && m_main_console) { + if (m_config) + { + for (util::xml::data_node const *node = m_config->get_first_child(); node; node = node->get_next_sibling()) + load_configuration(*node); + m_config.reset(); + } m_main_console->set_foreground(); if (winwindow_has_focus()) m_main_console->set_default_focus(); @@ -123,7 +195,7 @@ void debugger_windows::wait_for_debugger(device_t &device, bool firststop) show_all(); // run input polling to ensure that our status is in sync - downcast<windows_osd_interface&>(machine().osd()).poll_input(*m_machine); + downcast<windows_osd_interface&>(machine().osd()).poll_input_modules(false); // get and process messages MSG message; @@ -171,6 +243,14 @@ void debugger_windows::debugger_update() } +void debugger_windows::set_color_theme(int index) +{ + m_metrics->set_color_theme(index); + for (auto const &window : m_window_list) + window->redraw(); +} + + bool debugger_windows::seq_pressed() const { input_seq const &seq = m_machine->ioport().type_seq(IPT_UI_DEBUG_BREAK); @@ -222,7 +302,7 @@ bool debugger_windows::seq_pressed() const } -void debugger_windows::remove_window(debugwin_info &info) +void debugger_windows::remove_window(debugger::win::debugwin_info &info) { for (auto it = m_window_list.begin(); it != m_window_list.end(); ++it) if (it->get() == &info) { @@ -241,28 +321,162 @@ void debugger_windows::show_all() void debugger_windows::hide_all() { - SetForegroundWindow(std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window()); + SetForegroundWindow(dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window()); for (auto &info : m_window_list) info->hide(); } -template <typename T> T *debugger_windows::create_window() +void debugger_windows::stagger_window(HWND window, int width, int height) +{ + // get width/height for client size + RECT target; + target.left = 0; + target.top = 0; + target.right = width; + target.bottom = height; + if (!AdjustWindowRectEx(&target, GetWindowLong(window, GWL_STYLE), GetMenu(window) ? TRUE : FALSE,GetWindowLong(window, GWL_EXSTYLE))) + { + // really shouldn't end up here, but have to do something + SetWindowPos(window, HWND_TOP, m_next_window_pos.x, m_next_window_pos.y, width, height, SWP_SHOWWINDOW); + return; + } + target.right -= target.left; + target.bottom -= target.top; + target.left = target.top = 0; + + // get the work area for the nearest monitor to the target position + HMONITOR const mon = MonitorFromPoint(m_next_window_pos, MONITOR_DEFAULTTONEAREST); + if (mon) + { + MONITORINFO info; + std::memset(&info, 0, sizeof(info)); + info.cbSize = sizeof(info); + if (GetMonitorInfo(mon, &info)) + { + // restart cascade if necessary + if (((m_next_window_pos.x + target.right) > info.rcWork.right) || ((m_next_window_pos.y + target.bottom) > info.rcWork.bottom)) + { + m_next_window_pos.x = m_window_start_x += 16; + m_next_window_pos.y = info.rcWork.top + 100; + if ((m_next_window_pos.x + target.right) > info.rcWork.right) + m_next_window_pos.x = m_window_start_x = info.rcWork.left + 100; + } + } + } + + // move the window and adjust the next position + MoveWindow(window, m_next_window_pos.x, m_next_window_pos.y, target.right, target.bottom, FALSE); + SetWindowPos(window, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); + m_next_window_pos.x += 16; + m_next_window_pos.y += 16; +} + + +template <typename T> +T *debugger_windows::create_window() { // allocate memory std::unique_ptr<T> info = std::make_unique<T>(static_cast<debugger_windows_interface &>(*this)); if (info->is_valid()) { + T &result(*info); m_window_list.push_back(std::move(info)); - T *ptr = dynamic_cast<T*>(m_window_list.back().get()); - return ptr; + return &result; } return nullptr; } -#else /* not windows */ -MODULE_NOT_SUPPORTED(debugger_windows, OSD_DEBUG_PROVIDER, "windows") +void debugger_windows::config_load(config_type cfgtype, config_level cfglevel, util::xml::data_node const *parentnode) +{ + if (parentnode) + { + if (config_type::DEFAULT == cfgtype) + { + m_save_windows = 0 != parentnode->get_attribute_int(debugger::ATTR_DEBUGGER_SAVE_WINDOWS, m_save_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())); + } + else if (config_type::SYSTEM == cfgtype) + { + if (m_main_console) + { + load_configuration(*parentnode); + } + else + { + if (!m_config) + m_config = util::xml::file::create(); + parentnode->copy_into(*m_config); + } + } + } +} + + +void debugger_windows::config_save(config_type cfgtype, util::xml::data_node *parentnode) +{ + if (config_type::DEFAULT == cfgtype) + { + parentnode->set_attribute_int(debugger::ATTR_DEBUGGER_SAVE_WINDOWS, m_save_windows ? 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()); + } + else if (m_save_windows && (config_type::SYSTEM == cfgtype)) + { + for (auto &info : m_window_list) + info->save_configuration(*parentnode); + } +} + + +void debugger_windows::load_configuration(util::xml::data_node const &parentnode) +{ + for (util::xml::data_node const *node = parentnode.get_child(debugger::NODE_WINDOW); node; node = node->get_next_sibling(debugger::NODE_WINDOW)) + { + debugger::win::debugwin_info *win = nullptr; + switch (node->get_attribute_int(debugger::ATTR_WINDOW_TYPE, -1)) + { + case debugger::WINDOW_TYPE_CONSOLE: + m_main_console->restore_configuration_from_node(*node); + break; + case debugger::WINDOW_TYPE_MEMORY_VIEWER: + win = create_window<debugger::win::memorywin_info>(); + break; + case debugger::WINDOW_TYPE_DISASSEMBLY_VIEWER: + win = create_window<debugger::win::disasmwin_info>(); + break; + case debugger::WINDOW_TYPE_ERROR_LOG_VIEWER: + win = create_window<debugger::win::logwin_info>(); + break; + case debugger::WINDOW_TYPE_POINTS_VIEWER: + win = create_window<debugger::win::pointswin_info>(); + break; + case debugger::WINDOW_TYPE_DEVICES_VIEWER: + // not supported + break; + case debugger::WINDOW_TYPE_DEVICE_INFO_VIEWER: + // not supported + break; + default: + break; + } + if (win) + win->restore_configuration_from_node(*node); + } +} + +} // anonymous namespace + +} // namespace osd + +#else // not Windows + +namespace osd { namespace { MODULE_NOT_SUPPORTED(debugger_windows, OSD_DEBUG_PROVIDER, "windows") } } + #endif -MODULE_DEFINITION(DEBUG_WINDOWS, debugger_windows) +MODULE_DEFINITION(DEBUG_WINDOWS, osd::debugger_windows) diff --git a/src/osd/modules/debugger/none.cpp b/src/osd/modules/debugger/none.cpp index ef23fe04089..61bd1bbbd3e 100644 --- a/src/osd/modules/debugger/none.cpp +++ b/src/osd/modules/debugger/none.cpp @@ -8,24 +8,28 @@ #include "emu.h" #include "debug_module.h" -#include "modules/osdmodule.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debugger.h" + +namespace osd { + +namespace { + class debug_none : public osd_module, public debug_module { public: - debug_none() - : osd_module(OSD_DEBUG_PROVIDER, "none"), debug_module(), + debug_none() : + osd_module(OSD_DEBUG_PROVIDER, "none"), debug_module(), m_machine(nullptr) { } virtual ~debug_none() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual void exit() override { } virtual void init_debugger(running_machine &machine) override; @@ -50,4 +54,8 @@ void debug_none::debugger_update() { } -MODULE_DEFINITION(DEBUG_NONE, debug_none) +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(DEBUG_NONE, osd::debug_none) diff --git a/src/osd/modules/debugger/osx/debugcommandhistory.h b/src/osd/modules/debugger/osx/debugcommandhistory.h index f99767cb4be..e505aa4435c 100644 --- a/src/osd/modules/debugger/osx/debugcommandhistory.h +++ b/src/osd/modules/debugger/osx/debugcommandhistory.h @@ -8,6 +8,8 @@ #import "debugosx.h" +#include "util/utilfwd.h" + #import <Cocoa/Cocoa.h> @@ -32,4 +34,7 @@ - (void)reset; - (void)clear; +- (void)saveConfigurationToNode:(util::xml::data_node *)node; +- (void)restoreConfigurationFromNode:(util::xml::data_node const *)node; + @end diff --git a/src/osd/modules/debugger/osx/debugcommandhistory.mm b/src/osd/modules/debugger/osx/debugcommandhistory.mm index 74a34904ae7..e2292f973b0 100644 --- a/src/osd/modules/debugger/osx/debugcommandhistory.mm +++ b/src/osd/modules/debugger/osx/debugcommandhistory.mm @@ -12,6 +12,8 @@ #import "debugcommandhistory.h" +#include "util/xmlfile.h" + @implementation MAMEDebugCommandHistory @@ -54,9 +56,9 @@ - (void)add:(NSString *)entry { if (([history count] == 0) || ![[history objectAtIndex:0] isEqualToString:entry]) { - [history insertObject:entry atIndex:0]; - while ([history count] > length) + while ([history count] >= length) [history removeLastObject]; + [history insertObject:entry atIndex:0]; } position = 0; } @@ -110,4 +112,30 @@ [history removeAllObjects]; } + +- (void)saveConfigurationToNode:(util::xml::data_node *)node { + util::xml::data_node *const hist = node->add_child(osd::debugger::NODE_WINDOW_HISTORY, nullptr); + if (hist) { + for (NSInteger i = [history count]; 0 < i; --i) + hist->add_child(osd::debugger::NODE_HISTORY_ITEM, [[history objectAtIndex:(i - 1)] UTF8String]); + } +} + + +- (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { + [self clear]; + util::xml::data_node const *const hist = node->get_child(osd::debugger::NODE_WINDOW_HISTORY); + if (hist) { + util::xml::data_node const *item = hist->get_child(osd::debugger::NODE_HISTORY_ITEM); + while (item) { + if (item->get_value() && *item->get_value()) { + while ([history count] >= length) + [history removeLastObject]; + [history insertObject:[NSString stringWithUTF8String:item->get_value()] atIndex:0]; + } + item = item->get_next_sibling(osd::debugger::NODE_HISTORY_ITEM); + } + } +} + @end diff --git a/src/osd/modules/debugger/osx/debugconsole.mm b/src/osd/modules/debugger/osx/debugconsole.mm index c1689a8b855..9673dc852ab 100644 --- a/src/osd/modules/debugger/osx/debugconsole.mm +++ b/src/osd/modules/debugger/osx/debugconsole.mm @@ -142,19 +142,25 @@ NSRect const available = [[NSScreen mainScreen] visibleFrame]; NSSize const regCurrent = [regScroll frame].size; NSSize const regSize = [NSScrollView frameSizeForContentSize:[regView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[regScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[regScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; NSSize const dasmCurrent = [dasmScroll frame].size; NSSize const dasmSize = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[dasmScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[dasmScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; NSSize const consoleCurrent = [consoleContainer frame].size; NSSize consoleSize = [NSScrollView frameSizeForContentSize:[consoleView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[consoleScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[consoleScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; NSRect windowFrame = [window frame]; NSSize adjustment; @@ -380,32 +386,35 @@ - (void)loadConfiguration:(util::xml::data_node const *)parentnode { util::xml::data_node const *node = nullptr; - for (node = parentnode->get_child("window"); node; node = node->get_next_sibling("window")) + for (node = parentnode->get_child(osd::debugger::NODE_WINDOW); node; node = node->get_next_sibling(osd::debugger::NODE_WINDOW)) { MAMEDebugWindowHandler *win = nil; - switch (node->get_attribute_int("type", -1)) + switch (node->get_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, -1)) { - case MAME_DEBUGGER_WINDOW_TYPE_CONSOLE: + case osd::debugger::WINDOW_TYPE_CONSOLE: [self restoreConfigurationFromNode:node]; break; - case MAME_DEBUGGER_WINDOW_TYPE_MEMORY_VIEWER: + case osd::debugger::WINDOW_TYPE_MEMORY_VIEWER: win = [[MAMEMemoryViewer alloc] initWithMachine:*machine console:self]; break; - case MAME_DEBUGGER_WINDOW_TYPE_DISASSEMBLY_VIEWER: + case osd::debugger::WINDOW_TYPE_DISASSEMBLY_VIEWER: win = [[MAMEDisassemblyViewer alloc] initWithMachine:*machine console:self]; break; - case MAME_DEBUGGER_WINDOW_TYPE_ERROR_LOG_VIEWER: + case osd::debugger::WINDOW_TYPE_ERROR_LOG_VIEWER: win = [[MAMEErrorLogViewer alloc] initWithMachine:*machine console:self]; break; - case MAME_DEBUGGER_WINDOW_TYPE_POINTS_VIEWER: + case osd::debugger::WINDOW_TYPE_POINTS_VIEWER: win = [[MAMEPointsViewer alloc] initWithMachine:*machine console:self]; break; - case MAME_DEBUGGER_WINDOW_TYPE_DEVICES_VIEWER: + case osd::debugger::WINDOW_TYPE_DEVICES_VIEWER: win = [[MAMEDevicesViewer alloc] initWithMachine:*machine console:self]; break; - case MAME_DEBUGGER_WINDOW_TYPE_DEVICE_INFO_VIEWER: - // FIXME: needs device info on init, make another variant - //win = [[MAMEDeviceInfoViewer alloc] initWithMachine:*machine console:self]; + case osd::debugger::WINDOW_TYPE_DEVICE_INFO_VIEWER: + { + // FIXME: feels like a leaky abstraction, but device is needed for init + device_t *const device = machine->root_device().subdevice(node->get_attribute_string(osd::debugger::ATTR_WINDOW_DEVICE_TAG, ":")); + win = [[MAMEDeviceInfoViewer alloc] initWithDevice:(device ? *device : machine->root_device()) machine:*machine console:self]; + } break; default: break; @@ -423,34 +432,36 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_CONSOLE); - util::xml::data_node *const splits = node->add_child("splits", nullptr); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_CONSOLE); + util::xml::data_node *const splits = node->add_child(osd::debugger::NODE_WINDOW_SPLITS, nullptr); if (splits) { - splits->set_attribute_float("state", + splits->set_attribute_float(osd::debugger::ATTR_SPLITS_CONSOLE_STATE, [regSplit isSubviewCollapsed:[[regSplit subviews] objectAtIndex:0]] ? 0.0 : NSMaxX([[[regSplit subviews] objectAtIndex:0] frame])); - splits->set_attribute_float("disassembly", + splits->set_attribute_float(osd::debugger::ATTR_SPLITS_CONSOLE_DISASSEMBLY, [dasmSplit isSubviewCollapsed:[[dasmSplit subviews] objectAtIndex:0]] ? 0.0 : NSMaxY([[[dasmSplit subviews] objectAtIndex:0] frame])); } [dasmView saveConfigurationToNode:node]; + [history saveConfigurationToNode:node]; } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; - util::xml::data_node const *const splits = node->get_child("splits"); + util::xml::data_node const *const splits = node->get_child(osd::debugger::NODE_WINDOW_SPLITS); if (splits) { - [regSplit setPosition:splits->get_attribute_float("state", NSMaxX([[[regSplit subviews] objectAtIndex:0] frame])) + [regSplit setPosition:splits->get_attribute_float(osd::debugger::ATTR_SPLITS_CONSOLE_STATE, NSMaxX([[[regSplit subviews] objectAtIndex:0] frame])) ofDividerAtIndex:0]; - [dasmSplit setPosition:splits->get_attribute_float("disassembly", NSMaxY([[[dasmSplit subviews] objectAtIndex:0] frame])) + [dasmSplit setPosition:splits->get_attribute_float(osd::debugger::ATTR_SPLITS_CONSOLE_DISASSEMBLY, NSMaxY([[[dasmSplit subviews] objectAtIndex:0] frame])) ofDividerAtIndex:0]; } [dasmView restoreConfigurationFromNode:node]; + [history restoreConfigurationFromNode:node]; } diff --git a/src/osd/modules/debugger/osx/debugosx.h b/src/osd/modules/debugger/osx/debugosx.h index 9d48402c032..6e0a47089d9 100644 --- a/src/osd/modules/debugger/osx/debugosx.h +++ b/src/osd/modules/debugger/osx/debugosx.h @@ -6,8 +6,13 @@ // //============================================================ -#ifndef __SDL_DEBUGOSX__ -#define __SDL_DEBUGOSX__ +#ifndef MAME_OSD_DEBUGGER_OSX_DEBUGOSX_H +#define MAME_OSD_DEBUGGER_OSX_DEBUGOSX_H + +#pragma once + +#include "../xmlconfig.h" + #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 @@ -20,30 +25,6 @@ // standard Cocoa headers #import <Cocoa/Cocoa.h> -// workarounds for 10.6 warnings -#ifdef MAC_OS_X_VERSION_MAX_ALLOWED - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 - -@protocol NSWindowDelegate <NSObject> -@end - -@protocol NSSplitViewDelegate <NSObject> -@end - -@protocol NSControlTextEditingDelegate <NSObject> -@end - -@protocol NSTextFieldDelegate <NSControlTextEditingDelegate> -@end - -@protocol NSOutlineViewDataSource <NSObject> -@end - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060 - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED - #endif // __OBJC__ -#endif // __SDL_DEBUGOSX__ +#endif // MAME_OSD_DEBUGGER_OSX_DEBUGOSX_H diff --git a/src/osd/modules/debugger/osx/debugview.mm b/src/osd/modules/debugger/osx/debugview.mm index 8853be80622..ebf4ce257d5 100644 --- a/src/osd/modules/debugger/osx/debugview.mm +++ b/src/osd/modules/debugger/osx/debugview.mm @@ -51,6 +51,26 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) @implementation MAMEDebugView + (void)initialize { + // 10.15 and better get full adaptive Dark Mode support +#if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 + DefaultForeground = [[NSColor textColor] retain]; + ChangedForeground = [[NSColor systemRedColor] retain]; + CommentForeground = [[NSColor systemGreenColor] retain]; + // DCA_INVALID and DCA_DISABLED currently are not set by the core, so these 4 are unused + InvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:1.0 alpha:1.0] retain]; + DisabledChangedForeground = [[NSColor colorWithCalibratedRed:0.5 green:0.125 blue:0.125 alpha:1.0] retain]; + DisabledInvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.5 alpha:1.0] retain]; + DisabledCommentForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.25 blue:0.0 alpha:1.0] retain]; + + DefaultBackground = [[NSColor textBackgroundColor] retain]; + VisitedBackground = [[NSColor systemTealColor] retain]; + AncillaryBackground = [[NSColor unemphasizedSelectedContentBackgroundColor] retain]; + SelectedBackground = [[NSColor selectedContentBackgroundColor] retain]; + CurrentBackground = [[NSColor selectedControlColor] retain]; + SelectedCurrentBackground = [[NSColor unemphasizedSelectedContentBackgroundColor] retain]; + InactiveSelectedBackground = [[NSColor unemphasizedSelectedContentBackgroundColor] retain]; + InactiveSelectedCurrentBackground = [[NSColor systemGrayColor] retain]; +#else DefaultForeground = [[NSColor colorWithCalibratedWhite:0.0 alpha:1.0] retain]; ChangedForeground = [[NSColor colorWithCalibratedRed:0.875 green:0.0 blue:0.0 alpha:1.0] retain]; InvalidForeground = [[NSColor colorWithCalibratedRed:0.0 green:0.0 blue:1.0 alpha:1.0] retain]; @@ -67,6 +87,7 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) SelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.625 blue:0.875 alpha:1.0] retain]; InactiveSelectedBackground = [[NSColor colorWithCalibratedWhite:0.875 alpha:1.0] retain]; InactiveSelectedCurrentBackground = [[NSColor colorWithCalibratedRed:0.875 green:0.5 blue:0.625 alpha:1.0] retain]; +#endif NonWhiteCharacters = [[[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet] retain]; } @@ -493,22 +514,22 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) - (void)saveConfigurationToNode:(util::xml::data_node *)node { if (view->cursor_supported()) { - util::xml::data_node *const selection = node->add_child("selection", nullptr); + util::xml::data_node *const selection = node->add_child(osd::debugger::NODE_WINDOW_SELECTION, nullptr); if (selection) { debug_view_xy const pos = view->cursor_position(); - selection->set_attribute_int("visible", view->cursor_visible() ? 1 : 0); - selection->set_attribute_int("start_x", pos.x); - selection->set_attribute_int("start_y", pos.y); + selection->set_attribute_int(osd::debugger::ATTR_SELECTION_CURSOR_VISIBLE, view->cursor_visible() ? 1 : 0); + selection->set_attribute_int(osd::debugger::ATTR_SELECTION_CURSOR_X, pos.x); + selection->set_attribute_int(osd::debugger::ATTR_SELECTION_CURSOR_Y, pos.y); } } - util::xml::data_node *const scroll = node->add_child("scroll", nullptr); + util::xml::data_node *const scroll = node->add_child(osd::debugger::NODE_WINDOW_SCROLL, nullptr); if (scroll) { NSRect const visible = [self visibleRect]; - scroll->set_attribute_float("position_x", visible.origin.x); - scroll->set_attribute_float("position_y", visible.origin.y); + scroll->set_attribute_float(osd::debugger::ATTR_SCROLL_ORIGIN_X, visible.origin.x); + scroll->set_attribute_float(osd::debugger::ATTR_SCROLL_ORIGIN_Y, visible.origin.y); } } @@ -516,23 +537,23 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { if (view->cursor_supported()) { - util::xml::data_node const *const selection = node->get_child("selection"); + util::xml::data_node const *const selection = node->get_child(osd::debugger::NODE_WINDOW_SELECTION); if (selection) { debug_view_xy pos = view->cursor_position(); - view->set_cursor_visible(0 != selection->get_attribute_int("visible", view->cursor_visible() ? 1 : 0)); - pos.x = selection->get_attribute_int("start_x", pos.x); - pos.y = selection->get_attribute_int("start_y", pos.y); + view->set_cursor_visible(0 != selection->get_attribute_int(osd::debugger::ATTR_SELECTION_CURSOR_VISIBLE, view->cursor_visible() ? 1 : 0)); + pos.x = selection->get_attribute_int(osd::debugger::ATTR_SELECTION_CURSOR_X, pos.x); + pos.y = selection->get_attribute_int(osd::debugger::ATTR_SELECTION_CURSOR_Y, pos.y); view->set_cursor_position(pos); } } - util::xml::data_node const *const scroll = node->get_child("scroll"); + util::xml::data_node const *const scroll = node->get_child(osd::debugger::NODE_WINDOW_SCROLL); if (scroll) { NSRect visible = [self visibleRect]; - visible.origin.x = scroll->get_attribute_float("position_x", visible.origin.x); - visible.origin.y = scroll->get_attribute_float("position_y", visible.origin.y); + visible.origin.x = scroll->get_attribute_float(osd::debugger::ATTR_SCROLL_ORIGIN_X, visible.origin.x); + visible.origin.y = scroll->get_attribute_float(osd::debugger::ATTR_SCROLL_ORIGIN_Y, visible.origin.y); [self scrollRectToVisible:visible]; } } @@ -743,8 +764,8 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) - (void)mouseDown:(NSEvent *)event { NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; NSUInteger const modifiers = [event modifierFlags]; - view->process_click(((modifiers & NSCommandKeyMask) && [[self window] isMainWindow]) ? DCK_RIGHT_CLICK - : (modifiers & NSAlternateKeyMask) ? DCK_MIDDLE_CLICK + view->process_click(((modifiers & NSEventModifierFlagCommand) && [[self window] isMainWindow]) ? DCK_RIGHT_CLICK + : (modifiers & NSEventModifierFlagOption) ? DCK_MIDDLE_CLICK : DCK_LEFT_CLICK, [self convertLocation:location]); } @@ -755,8 +776,8 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) NSPoint const location = [self convertPoint:[event locationInWindow] fromView:nil]; NSUInteger const modifiers = [event modifierFlags]; if (view->cursor_supported() - && !(modifiers & NSAlternateKeyMask) - && (!(modifiers & NSCommandKeyMask) || ![[self window] isMainWindow])) + && !(modifiers & NSEventModifierFlagOption) + && (!(modifiers & NSEventModifierFlagCommand) || ![[self window] isMainWindow])) { view->set_cursor_position([self convertLocation:location]); view->set_cursor_visible(true); @@ -783,34 +804,34 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) if ([str length] == 1) { - if (modifiers & NSNumericPadKeyMask) + if (modifiers & NSEventModifierFlagNumericPad) { switch ([str characterAtIndex:0]) { case NSUpArrowFunctionKey: - if (modifiers & NSCommandKeyMask) + if (modifiers & NSEventModifierFlagCommand) view->process_char(DCH_CTRLHOME); else view->process_char(DCH_UP); return; case NSDownArrowFunctionKey: - if (modifiers & NSCommandKeyMask) + if (modifiers & NSEventModifierFlagCommand) view->process_char(DCH_CTRLEND); else view->process_char(DCH_DOWN); return; case NSLeftArrowFunctionKey: - if (modifiers & NSCommandKeyMask) + if (modifiers & NSEventModifierFlagCommand) [self typeCharacterAndScrollToCursor:DCH_HOME]; - else if (modifiers & NSAlternateKeyMask) + else if (modifiers & NSEventModifierFlagOption) [self typeCharacterAndScrollToCursor:DCH_CTRLLEFT]; else [self typeCharacterAndScrollToCursor:DCH_LEFT]; return; case NSRightArrowFunctionKey: - if (modifiers & NSCommandKeyMask) + if (modifiers & NSEventModifierFlagCommand) [self typeCharacterAndScrollToCursor:DCH_END]; - else if (modifiers & NSAlternateKeyMask) + else if (modifiers & NSEventModifierFlagOption) [self typeCharacterAndScrollToCursor:DCH_CTRLRIGHT]; else [self typeCharacterAndScrollToCursor:DCH_RIGHT]; @@ -820,18 +841,18 @@ static void debugwin_view_update(debug_view &view, void *osdprivate) return; } } - else if (modifiers & NSFunctionKeyMask) + else if (modifiers & NSEventModifierFlagFunction) { switch ([str characterAtIndex:0]) { case NSPageUpFunctionKey: - if (modifiers & NSAlternateKeyMask) + if (modifiers & NSEventModifierFlagOption) { view->process_char(DCH_PUP); return; } case NSPageDownFunctionKey: - if (modifiers & NSAlternateKeyMask) + if (modifiers & NSEventModifierFlagOption) { view->process_char(DCH_PDOWN); return; diff --git a/src/osd/modules/debugger/osx/debugwindowhandler.h b/src/osd/modules/debugger/osx/debugwindowhandler.h index c48bbb031b2..00c7327f2ba 100644 --- a/src/osd/modules/debugger/osx/debugwindowhandler.h +++ b/src/osd/modules/debugger/osx/debugwindowhandler.h @@ -23,19 +23,6 @@ extern NSString *const MAMEAuxiliaryDebugWindowWillCloseNotification; extern NSString *const MAMESaveDebuggerConfigurationNotification; -// for compatibility with the Qt debugger -enum -{ - MAME_DEBUGGER_WINDOW_TYPE_CONSOLE = 1, - MAME_DEBUGGER_WINDOW_TYPE_MEMORY_VIEWER, - MAME_DEBUGGER_WINDOW_TYPE_DISASSEMBLY_VIEWER, - MAME_DEBUGGER_WINDOW_TYPE_ERROR_LOG_VIEWER, - MAME_DEBUGGER_WINDOW_TYPE_POINTS_VIEWER, - MAME_DEBUGGER_WINDOW_TYPE_DEVICES_VIEWER, - MAME_DEBUGGER_WINDOW_TYPE_DEVICE_INFO_VIEWER -}; - - @interface MAMEDebugWindowHandler : NSObject <NSWindowDelegate> { NSWindow *window; diff --git a/src/osd/modules/debugger/osx/debugwindowhandler.mm b/src/osd/modules/debugger/osx/debugwindowhandler.mm index ec79887e308..1341f420ed9 100644 --- a/src/osd/modules/debugger/osx/debugwindowhandler.mm +++ b/src/osd/modules/debugger/osx/debugwindowhandler.mm @@ -79,7 +79,7 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo [[stepMenu addItemWithTitle:@"Out" action:@selector(debugStepOut:) keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF10FunctionKey]] - setKeyEquivalentModifierMask:NSShiftKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagShift]; NSMenuItem *resetParentItem = [menu addItemWithTitle:@"Reset" action:NULL keyEquivalent:@""]; NSMenu *resetMenu = [[NSMenu alloc] initWithTitle:@"Reset"]; @@ -92,7 +92,7 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo [[resetMenu addItemWithTitle:@"Hard" action:@selector(debugHardReset:) keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF3FunctionKey]] - setKeyEquivalentModifierMask:NSShiftKeyMask]; + setKeyEquivalentModifierMask:NSEventModifierFlagShift]; [menu addItem:[NSMenuItem separatorItem]]; @@ -127,7 +127,7 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo NSPopUpButton *actionButton = [[NSPopUpButton alloc] initWithFrame:frame pullsDown:YES]; [actionButton setTitle:@""]; [actionButton addItemWithTitle:@""]; - [actionButton setBezelStyle:NSShadowlessSquareBezelStyle]; + [actionButton setBezelStyle:NSBezelStyleShadowlessSquare]; [actionButton setFocusRingType:NSFocusRingTypeNone]; [[actionButton cell] setArrowPosition:NSPopUpArrowAtCenter]; [[self class] addCommonActionItems:[actionButton menu]]; @@ -140,10 +140,10 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo return nil; window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 320, 240) - styleMask:(NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask | - NSResizableWindowMask) + styleMask:(NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable | + NSWindowStyleMaskResizable) backing:NSBackingStoreBuffered defer:YES]; [window setReleasedWhenClosed:NO]; @@ -276,7 +276,7 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo if (m == machine) { util::xml::data_node *parentnode = (util::xml::data_node *)[[[notification userInfo] objectForKey:@"MAMEDebugParentNode"] pointerValue]; - util::xml::data_node *node = parentnode->add_child("window", nullptr); + util::xml::data_node *node = parentnode->add_child(osd::debugger::NODE_WINDOW, nullptr); if (node) [self saveConfigurationToNode:node]; } @@ -285,19 +285,19 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo - (void)saveConfigurationToNode:(util::xml::data_node *)node { NSRect frame = [window frame]; - node->set_attribute_float("position_x", frame.origin.x); - node->set_attribute_float("position_y", frame.origin.y); - node->set_attribute_float("size_x", frame.size.width); - node->set_attribute_float("size_y", frame.size.height); + node->set_attribute_float(osd::debugger::ATTR_WINDOW_POSITION_X, frame.origin.x); + node->set_attribute_float(osd::debugger::ATTR_WINDOW_POSITION_Y, frame.origin.y); + node->set_attribute_float(osd::debugger::ATTR_WINDOW_WIDTH, frame.size.width); + node->set_attribute_float(osd::debugger::ATTR_WINDOW_HEIGHT, frame.size.height); } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { NSRect frame = [window frame]; - frame.origin.x = node->get_attribute_float("position_x", frame.origin.x); - frame.origin.y = node->get_attribute_float("position_y", frame.origin.y); - frame.size.width = node->get_attribute_float("size_x", frame.size.width); - frame.size.height = node->get_attribute_float("size_y", frame.size.height); + frame.origin.x = node->get_attribute_float(osd::debugger::ATTR_WINDOW_POSITION_X, frame.origin.x); + frame.origin.y = node->get_attribute_float(osd::debugger::ATTR_WINDOW_POSITION_Y, frame.origin.y); + frame.size.width = node->get_attribute_float(osd::debugger::ATTR_WINDOW_WIDTH, frame.size.width); + frame.size.height = node->get_attribute_float(osd::debugger::ATTR_WINDOW_HEIGHT, frame.size.height); NSSize min = [window minSize]; frame.size.width = std::max(frame.size.width, min.width); @@ -493,15 +493,17 @@ NSString *const MAMESaveDebuggerConfigurationNotification = @"MAMESaveDebuggerCo - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->add_child("expression", [[self expression] UTF8String]); + node->add_child(osd::debugger::NODE_WINDOW_EXPRESSION, [[self expression] UTF8String]); + [history saveConfigurationToNode:node]; } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; - util::xml::data_node const *const expr = node->get_child("expression"); + util::xml::data_node const *const expr = node->get_child(osd::debugger::NODE_WINDOW_EXPRESSION); if (expr && expr->get_value()) [self setExpression:[NSString stringWithUTF8String:expr->get_value()]]; + [history restoreConfigurationFromNode:node]; } @end diff --git a/src/osd/modules/debugger/osx/deviceinfoviewer.mm b/src/osd/modules/debugger/osx/deviceinfoviewer.mm index 61fdd8c8613..12f1ea1957a 100644 --- a/src/osd/modules/debugger/osx/deviceinfoviewer.mm +++ b/src/osd/modules/debugger/osx/deviceinfoviewer.mm @@ -54,14 +54,14 @@ - (NSTextField *)makeLabel:(NSString *)text { NSTextField *const result = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 14)]; [result setAutoresizingMask:(NSViewMaxYMargin | NSViewMaxXMargin)]; - [[result cell] setControlSize:NSSmallControlSize]; + [[result cell] setControlSize:NSControlSizeSmall]; [result setEditable:NO]; [result setSelectable:NO]; [result setBezeled:NO]; [result setBordered:NO]; [result setDrawsBackground:NO]; - [result setAlignment:NSRightTextAlignment]; - [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + [result setAlignment:NSTextAlignmentRight]; + [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSControlSizeSmall]]]; [result setStringValue:text]; [result sizeToFit]; return result; @@ -71,14 +71,14 @@ - (NSTextField *)makeField:(NSString *)text { NSTextField *const result = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 100, 14)]; [result setAutoresizingMask:(NSViewWidthSizable | NSViewMaxYMargin)]; - [[result cell] setControlSize:NSSmallControlSize]; + [[result cell] setControlSize:NSControlSizeSmall]; [result setEditable:NO]; [result setSelectable:YES]; [result setBezeled:NO]; [result setBordered:NO]; [result setDrawsBackground:NO]; - [result setAlignment:NSLeftTextAlignment]; - [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + [result setAlignment:NSTextAlignmentLeft]; + [result setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSControlSizeSmall]]]; [result setStringValue:text]; [result sizeToFit]; return result; @@ -210,9 +210,11 @@ // create a scroll view for holding everything NSSize desired = [NSScrollView frameSizeForContentSize:[contentView frame].size - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:NSNoBorder]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:NSNoBorder + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; [window setContentSize:desired]; contentScroll = [[NSScrollView alloc] initWithFrame:[[window contentView] bounds]]; [contentScroll setDrawsBackground:NO]; @@ -237,7 +239,8 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_DEVICE_INFO_VIEWER); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_DEVICE_INFO_VIEWER); + node->set_attribute(osd::debugger::ATTR_WINDOW_DEVICE_TAG, device->tag()); } @end diff --git a/src/osd/modules/debugger/osx/devicesviewer.mm b/src/osd/modules/debugger/osx/devicesviewer.mm index 23dba724b9d..02f63a41a6b 100644 --- a/src/osd/modules/debugger/osx/devicesviewer.mm +++ b/src/osd/modules/debugger/osx/devicesviewer.mm @@ -161,9 +161,11 @@ // calculate the optimal size for everything NSSize const desired = [NSScrollView frameSizeForContentSize:NSMakeSize(480, 320) - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[devicesScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[devicesScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; [self cascadeWindowWithDesiredSize:desired forView:devicesScroll]; // don't forget the result @@ -185,7 +187,7 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_DEVICES_VIEWER); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_DEVICES_VIEWER); } diff --git a/src/osd/modules/debugger/osx/disassemblyview.mm b/src/osd/modules/debugger/osx/disassemblyview.mm index 2e230c27e1c..c501d17230a 100644 --- a/src/osd/modules/debugger/osx/disassemblyview.mm +++ b/src/osd/modules/debugger/osx/disassemblyview.mm @@ -75,7 +75,7 @@ item = [menu addItemWithTitle:@"Disable Breakpoint" action:@selector(debugToggleBreakpointEnable:) keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey]]; - [item setKeyEquivalentModifierMask:NSShiftKeyMask]; + [item setKeyEquivalentModifierMask:NSEventModifierFlagShift]; [menu addItem:[NSMenuItem separatorItem]]; @@ -214,7 +214,7 @@ action:@selector(debugToggleBreakpointEnable:) keyEquivalent:[NSString stringWithFormat:@"%C", (short)NSF9FunctionKey] atIndex:index++]; - [disableItem setKeyEquivalentModifierMask:NSShiftKeyMask]; + [disableItem setKeyEquivalentModifierMask:NSEventModifierFlagShift]; NSMenu *runMenu = [[menu itemWithTitle:@"Run"] submenu]; NSMenuItem *runItem; @@ -274,14 +274,14 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; debug_view_disasm *const dasmView = downcast<debug_view_disasm *>(view); - node->set_attribute_int("rightbar", dasmView->right_column()); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmView->right_column()); } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; debug_view_disasm *const dasmView = downcast<debug_view_disasm *>(view); - dasmView->set_right_column((disasm_right_column)node->get_attribute_int("rightbar", dasmView->right_column())); + dasmView->set_right_column((disasm_right_column)node->get_attribute_int(osd::debugger::ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmView->right_column())); } @end diff --git a/src/osd/modules/debugger/osx/disassemblyviewer.mm b/src/osd/modules/debugger/osx/disassemblyviewer.mm index 1f286a6bfc4..20ec457f628 100644 --- a/src/osd/modules/debugger/osx/disassemblyviewer.mm +++ b/src/osd/modules/debugger/osx/disassemblyviewer.mm @@ -49,7 +49,7 @@ expressionFrame.size.width, 0)]; [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; - [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; + [subviewButton setBezelStyle:NSBezelStyleShadowlessSquare]; [subviewButton setFocusRingType:NSFocusRingTypeNone]; [subviewButton setFont:defaultFont]; [subviewButton setTarget:self]; @@ -120,9 +120,11 @@ // calculate the optimal size for everything NSSize const desired = [NSScrollView frameSizeForContentSize:[dasmView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[dasmScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[dasmScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; [self cascadeWindowWithDesiredSize:desired forView:dasmScroll]; // don't forget the result @@ -182,7 +184,7 @@ // if it doesn't exist, add a new one if (bp == nullptr) { - uint32_t const bpnum = device.debug()->breakpoint_set(address, nullptr, nullptr); + uint32_t const bpnum = device.debug()->breakpoint_set(address); machine->debugger().console().printf("Breakpoint %X set\n", bpnum); } else @@ -232,15 +234,15 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_DISASSEMBLY_VIEWER); - node->set_attribute_int("cpu", [dasmView selectedSubviewIndex]); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_DISASSEMBLY_VIEWER); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_DISASSEMBLY_CPU, [dasmView selectedSubviewIndex]); [dasmView saveConfigurationToNode:node]; } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; - int const region = node->get_attribute_int("cpu", [dasmView selectedSubviewIndex]); + int const region = node->get_attribute_int(osd::debugger::ATTR_WINDOW_DISASSEMBLY_CPU, [dasmView selectedSubviewIndex]); [dasmView selectSubviewAtIndex:region]; [window setTitle:[NSString stringWithFormat:@"Disassembly: %@", [dasmView selectedSubviewName]]]; [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[dasmView selectedSubviewIndex]]]; diff --git a/src/osd/modules/debugger/osx/errorlogviewer.mm b/src/osd/modules/debugger/osx/errorlogviewer.mm index 1fd12c9c9c1..68d6f312b7c 100644 --- a/src/osd/modules/debugger/osx/errorlogviewer.mm +++ b/src/osd/modules/debugger/osx/errorlogviewer.mm @@ -43,11 +43,13 @@ // calculate the optimal size for everything { NSSize desired = [NSScrollView frameSizeForContentSize:[logView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[logScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[logScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; - // this thing starts with no content, so its prefered height may be very small + // this thing starts with no content, so its preferred height may be very small desired.height = std::max(desired.height, CGFloat(240)); [self cascadeWindowWithDesiredSize:desired forView:logScroll]; } @@ -64,7 +66,7 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_ERROR_LOG_VIEWER); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_ERROR_LOG_VIEWER); } @end diff --git a/src/osd/modules/debugger/osx/memoryview.mm b/src/osd/modules/debugger/osx/memoryview.mm index 03055bd7daf..24521e81ba4 100644 --- a/src/osd/modules/debugger/osx/memoryview.mm +++ b/src/osd/modules/debugger/osx/memoryview.mm @@ -212,20 +212,22 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; debug_view_memory *const memView = downcast<debug_view_memory *>(view); - node->set_attribute_int("reverse", memView->reverse() ? 1 : 0); - node->set_attribute_int("addressmode", memView->physical() ? 1 : 0); - node->set_attribute_int("dataformat", int(memView->get_data_format())); - node->set_attribute_int("rowchunks", memView->chunks_per_row()); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_REVERSE_COLUMNS, memView->reverse() ? 1 : 0); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_ADDRESS_MODE, memView->physical() ? 1 : 0); + node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_ADDRESS_RADIX, memView->address_radix()); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_DATA_FORMAT, int(memView->get_data_format())); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_ROW_CHUNKS, memView->chunks_per_row()); } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; debug_view_memory *const memView = downcast<debug_view_memory *>(view); - memView->set_reverse(0 != node->get_attribute_int("reverse", memView->reverse() ? 1 : 0)); - memView->set_physical(0 != node->get_attribute_int("addressmode", memView->physical() ? 1 : 0)); - memView->set_data_format(debug_view_memory::data_format(node->get_attribute_int("dataformat", int(memView->get_data_format())))); - memView->set_chunks_per_row(node->get_attribute_int("rowchunks", memView->chunks_per_row())); + memView->set_reverse(0 != node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_REVERSE_COLUMNS, memView->reverse() ? 1 : 0)); + memView->set_physical(0 != node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_ADDRESS_MODE, memView->physical() ? 1 : 0)); + memView->set_address_radix(node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_ADDRESS_RADIX, memView->address_radix())); + memView->set_data_format(debug_view_memory::data_format(node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_DATA_FORMAT, int(memView->get_data_format())))); + memView->set_chunks_per_row(node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_ROW_CHUNKS, memView->chunks_per_row())); } diff --git a/src/osd/modules/debugger/osx/memoryviewer.mm b/src/osd/modules/debugger/osx/memoryviewer.mm index b1064aee69f..1d45650dcbb 100644 --- a/src/osd/modules/debugger/osx/memoryviewer.mm +++ b/src/osd/modules/debugger/osx/memoryviewer.mm @@ -46,7 +46,7 @@ // create the subview popup subviewButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinXMargin | NSViewMinYMargin)]; - [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; + [subviewButton setBezelStyle:NSBezelStyleShadowlessSquare]; [subviewButton setFocusRingType:NSFocusRingTypeNone]; [subviewButton setFont:defaultFont]; [subviewButton setTarget:self]; @@ -118,9 +118,11 @@ // calculate the optimal size for everything NSSize const desired = [NSScrollView frameSizeForContentSize:[memoryView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[memoryScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[memoryScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; [self cascadeWindowWithDesiredSize:desired forView:memoryScroll]; // don't forget the result @@ -178,15 +180,15 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_MEMORY_VIEWER); - node->set_attribute_int("memoryregion", [memoryView selectedSubviewIndex]); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_MEMORY_VIEWER); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_REGION, [memoryView selectedSubviewIndex]); [memoryView saveConfigurationToNode:node]; } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; - int const region = node->get_attribute_int("memoryregion", [memoryView selectedSubviewIndex]); + int const region = node->get_attribute_int(osd::debugger::ATTR_WINDOW_MEMORY_REGION, [memoryView selectedSubviewIndex]); [memoryView selectSubviewAtIndex:region]; [window setTitle:[NSString stringWithFormat:@"Memory: %@", [memoryView selectedSubviewName]]]; [subviewButton selectItemAtIndex:[subviewButton indexOfItemWithTag:[memoryView selectedSubviewIndex]]]; diff --git a/src/osd/modules/debugger/osx/pointsviewer.mm b/src/osd/modules/debugger/osx/pointsviewer.mm index 0399fb10d1b..8b2f69c50b5 100644 --- a/src/osd/modules/debugger/osx/pointsviewer.mm +++ b/src/osd/modules/debugger/osx/pointsviewer.mm @@ -33,7 +33,7 @@ // create the subview popup subviewButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 19)]; [subviewButton setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)]; - [subviewButton setBezelStyle:NSShadowlessSquareBezelStyle]; + [subviewButton setBezelStyle:NSBezelStyleShadowlessSquare]; [subviewButton setFocusRingType:NSFocusRingTypeNone]; [subviewButton setFont:defaultFont]; [subviewButton setTarget:self]; @@ -139,17 +139,23 @@ // calculate the optimal size for everything NSSize const breakDesired = [NSScrollView frameSizeForContentSize:[breakView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[breakScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[breakScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; NSSize const watchDesired = [NSScrollView frameSizeForContentSize:[watchView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[watchScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + borderType:[watchScroll borderType] + controlSize:NSControlSizeRegular + scrollerStyle:NSScrollerStyleOverlay]; NSSize const registerDesired = [NSScrollView frameSizeForContentSize:[registerView maximumFrameSize] - hasHorizontalScroller:YES - hasVerticalScroller:YES - borderType:[registerScroll borderType]]; + horizontalScrollerClass:[NSScroller class] + verticalScrollerClass:[NSScroller class] + 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 })); [self cascadeWindowWithDesiredSize:desired forView:tabs]; @@ -172,14 +178,14 @@ - (void)saveConfigurationToNode:(util::xml::data_node *)node { [super saveConfigurationToNode:node]; - node->set_attribute_int("type", MAME_DEBUGGER_WINDOW_TYPE_POINTS_VIEWER); - node->set_attribute_int("bwtype", [tabs indexOfTabViewItem:[tabs selectedTabViewItem]]); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_TYPE, osd::debugger::WINDOW_TYPE_POINTS_VIEWER); + node->set_attribute_int(osd::debugger::ATTR_WINDOW_POINTS_TYPE, [tabs indexOfTabViewItem:[tabs selectedTabViewItem]]); } - (void)restoreConfigurationFromNode:(util::xml::data_node const *)node { [super restoreConfigurationFromNode:node]; - int const tab = node->get_attribute_int("bwtype", [tabs indexOfTabViewItem:[tabs selectedTabViewItem]]); + int const tab = node->get_attribute_int(osd::debugger::ATTR_WINDOW_POINTS_TYPE, [tabs indexOfTabViewItem:[tabs selectedTabViewItem]]); if ((0 <= tab) && ([tabs numberOfTabViewItems] > tab)) { [subviewButton selectItemAtIndex:tab]; diff --git a/src/osd/modules/debugger/qt/breakpointswindow.cpp b/src/osd/modules/debugger/qt/breakpointswindow.cpp index 6af8fe7ba36..16c8af6bdb3 100644 --- a/src/osd/modules/debugger/qt/breakpointswindow.cpp +++ b/src/osd/modules/debugger/qt/breakpointswindow.cpp @@ -6,15 +6,23 @@ #include "debug/debugcon.h" #include "debug/debugcpu.h" +#include "util/xmlfile.h" + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include <QtGui/QActionGroup> +#else #include <QtWidgets/QActionGroup> +#endif #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QMenu> #include <QtWidgets/QMenuBar> #include <QtWidgets/QVBoxLayout> -BreakpointsWindow::BreakpointsWindow(running_machine &machine, QWidget *parent) : - WindowQt(machine, nullptr) +namespace osd::debugger::qt { + +BreakpointsWindow::BreakpointsWindow(DebuggerQt &debugger, QWidget *parent) : + WindowQt(debugger, nullptr) { setWindowTitle("Debug: All Breakpoints"); @@ -79,6 +87,47 @@ BreakpointsWindow::~BreakpointsWindow() } +void BreakpointsWindow::restoreConfiguration(util::xml::data_node const &node) +{ + WindowQt::restoreConfiguration(node); + + auto const type = node.get_attribute_int(ATTR_WINDOW_POINTS_TYPE, -1); + QActionGroup *typeGroup = findChild<QActionGroup *>("typegroup"); + if ((0 <= type) && (typeGroup->actions().size() > type)) + typeGroup->actions()[type]->trigger(); + + m_breakpointsView->restoreConfigurationFromNode(node); + +} + + +void BreakpointsWindow::saveConfigurationToNode(util::xml::data_node &node) +{ + WindowQt::saveConfigurationToNode(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_POINTS_VIEWER); + if (m_breakpointsView) + { + switch (m_breakpointsView->view()->type()) + { + case DVT_BREAK_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 0); + break; + case DVT_WATCH_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 1); + break; + case DVT_REGISTER_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 2); + break; + default: + break; + } + } + + m_breakpointsView->saveConfigurationToNode(node); +} + + void BreakpointsWindow::typeChanged(QAction* changedTo) { // Clean @@ -107,45 +156,4 @@ void BreakpointsWindow::typeChanged(QAction* changedTo) layout->addWidget(m_breakpointsView); } - - -//========================================================================= -// BreakpointsWindowQtConfig -//========================================================================= -void BreakpointsWindowQtConfig::buildFromQWidget(QWidget *widget) -{ - WindowQtConfig::buildFromQWidget(widget); - BreakpointsWindow *window = dynamic_cast<BreakpointsWindow *>(widget); - - QActionGroup* typeGroup = window->findChild<QActionGroup*>("typegroup"); - if (typeGroup->checkedAction()->text() == "Breakpoints") - m_bwType = 0; - else if (typeGroup->checkedAction()->text() == "Watchpoints") - m_bwType = 1; - else if (typeGroup->checkedAction()->text() == "Registerpoints") - m_bwType = 2; -} - - -void BreakpointsWindowQtConfig::applyToQWidget(QWidget* widget) -{ - WindowQtConfig::applyToQWidget(widget); - BreakpointsWindow *window = dynamic_cast<BreakpointsWindow *>(widget); - - QActionGroup *typeGroup = window->findChild<QActionGroup *>("typegroup"); - typeGroup->actions()[m_bwType]->trigger(); -} - - -void BreakpointsWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const -{ - WindowQtConfig::addToXmlDataNode(node); - node.set_attribute_int("bwtype", m_bwType); -} - - -void BreakpointsWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); - m_bwType = node.get_attribute_int("bwtype", m_bwType); -} +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/breakpointswindow.h b/src/osd/modules/debugger/qt/breakpointswindow.h index fab5ea149e9..0a07b4facfb 100644 --- a/src/osd/modules/debugger/qt/breakpointswindow.h +++ b/src/osd/modules/debugger/qt/breakpointswindow.h @@ -7,6 +7,8 @@ #include "windowqt.h" +namespace osd::debugger::qt { + //============================================================ // The Breakpoints Window. //============================================================ @@ -15,9 +17,14 @@ class BreakpointsWindow : public WindowQt Q_OBJECT public: - BreakpointsWindow(running_machine &machine, QWidget *parent = nullptr); + BreakpointsWindow(DebuggerQt &debugger, QWidget *parent = nullptr); virtual ~BreakpointsWindow(); + virtual void restoreConfiguration(util::xml::data_node const &node) override; + +protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; + private slots: void typeChanged(QAction *changedTo); @@ -26,28 +33,6 @@ private: DebuggerView *m_breakpointsView; }; - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class BreakpointsWindowQtConfig : public WindowQtConfig -{ -public: - BreakpointsWindowQtConfig() : - WindowQtConfig(WIN_TYPE_BREAK_POINTS), - m_bwType(0) - { - } - - ~BreakpointsWindowQtConfig() {} - - // Settings - int m_bwType; - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); -}; +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_BREAKPOINTSWINDOW_H diff --git a/src/osd/modules/debugger/qt/dasmwindow.cpp b/src/osd/modules/debugger/qt/dasmwindow.cpp index 44ce8bac988..144e2f32f5d 100644 --- a/src/osd/modules/debugger/qt/dasmwindow.cpp +++ b/src/osd/modules/debugger/qt/dasmwindow.cpp @@ -8,15 +8,26 @@ #include "debug/dvdisasm.h" #include "debug/points.h" +#include "util/xmlfile.h" + +#include <QtGui/QKeyEvent> #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QVBoxLayout> +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#else #include <QtWidgets/QAction> +#endif #include <QtWidgets/QMenu> #include <QtWidgets/QMenuBar> -DasmWindow::DasmWindow(running_machine &machine, QWidget *parent) : - WindowQt(machine, nullptr) +namespace osd::debugger::qt { + +DasmWindow::DasmWindow(DebuggerQt &debugger, QWidget *parent) : + WindowQt(debugger, nullptr), + m_inputHistory() { setWindowTitle("Debug: Disassembly View"); @@ -37,6 +48,8 @@ DasmWindow::DasmWindow(running_machine &machine, QWidget *parent) : // The input edit m_inputEdit = new QLineEdit(topSubFrame); connect(m_inputEdit, &QLineEdit::returnPressed, this, &DasmWindow::expressionSubmitted); + connect(m_inputEdit, &QLineEdit::textEdited, this, &DasmWindow::expressionEdited); + m_inputEdit->installEventFilter(this); // The cpu combo box m_cpuComboBox = new QComboBox(topSubFrame); @@ -49,7 +62,7 @@ DasmWindow::DasmWindow(running_machine &machine, QWidget *parent) : connect(m_dasmView, &DebuggerView::updated, this, &DasmWindow::dasmViewUpdated); // Force a recompute of the disassembly region - downcast<debug_view_disasm *>(m_dasmView->view())->set_expression("curpc"); + m_dasmView->view<debug_view_disasm>()->set_expression("curpc"); // Populate the combo box & set the proper CPU populateComboBox(); @@ -78,7 +91,7 @@ DasmWindow::DasmWindow(running_machine &machine, QWidget *parent) : m_breakpointEnableAct = new QAction("Disable Breakpoint at Cursor", this); m_runToCursorAct = new QAction("Run to Cursor", this); m_breakpointToggleAct->setShortcut(Qt::Key_F9); - m_breakpointEnableAct->setShortcut(Qt::SHIFT + Qt::Key_F9); + m_breakpointEnableAct->setShortcut(Qt::SHIFT | Qt::Key_F9); m_runToCursorAct->setShortcut(Qt::Key_F4); connect(m_breakpointToggleAct, &QAction::triggered, this, &DasmWindow::toggleBreakpointAtCursor); connect(m_breakpointEnableAct, &QAction::triggered, this, &DasmWindow::enableBreakpointAtCursor); @@ -90,6 +103,9 @@ DasmWindow::DasmWindow(running_machine &machine, QWidget *parent) : QAction *rightActRaw = new QAction("Raw Opcodes", this); QAction *rightActEncrypted = new QAction("Encrypted Opcodes", this); QAction *rightActComments = new QAction("Comments", this); + rightActRaw->setData(int(DASM_RIGHTCOL_RAW)); + rightActEncrypted->setData(int(DASM_RIGHTCOL_ENCRYPTED)); + rightActComments->setData(int(DASM_RIGHTCOL_COMMENTS)); rightActRaw->setCheckable(true); rightActEncrypted->setCheckable(true); rightActComments->setCheckable(true); @@ -117,6 +133,99 @@ DasmWindow::~DasmWindow() } +void DasmWindow::restoreConfiguration(util::xml::data_node const &node) +{ + WindowQt::restoreConfiguration(node); + + debug_view_disasm &dasmview = *m_dasmView->view<debug_view_disasm>(); + + auto const cpu = node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_CPU, m_dasmView->sourceIndex()); + if ((0 <= cpu) && (m_cpuComboBox->count() > cpu)) + m_cpuComboBox->setCurrentIndex(cpu); + + auto const rightbar = node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()); + QActionGroup *const rightBarGroup = findChild<QActionGroup *>("rightbargroup"); + for (QAction *action : rightBarGroup->actions()) + { + if (action->data().toInt() == rightbar) + { + action->trigger(); + break; + } + } + + util::xml::data_node const *const expression = node.get_child(NODE_WINDOW_EXPRESSION); + if (expression && expression->get_value()) + { + m_inputEdit->setText(QString::fromUtf8(expression->get_value())); + expressionSubmitted(); + } + + m_dasmView->restoreConfigurationFromNode(node); + m_inputHistory.restoreConfigurationFromNode(node); +} + + +void DasmWindow::saveConfigurationToNode(util::xml::data_node &node) +{ + WindowQt::saveConfigurationToNode(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_DISASSEMBLY_VIEWER); + + debug_view_disasm &dasmview = *m_dasmView->view<debug_view_disasm>(); + node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_CPU, m_dasmView->sourceIndex()); + node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()); + node.add_child(NODE_WINDOW_EXPRESSION, dasmview.expression()); + + m_dasmView->saveConfigurationToNode(node); + m_inputHistory.saveConfigurationToNode(node); +} + + +// Used to intercept the user hitting the up arrow in the input widget +bool DasmWindow::eventFilter(QObject *obj, QEvent *event) +{ + // Only filter keypresses + if (event->type() != QEvent::KeyPress) + return QObject::eventFilter(obj, event); + + QKeyEvent const &keyEvent = *static_cast<QKeyEvent *>(event); + + // Catch up & down keys + if (keyEvent.key() == Qt::Key_Escape) + { + m_inputEdit->setText(QString::fromUtf8(m_dasmView->view<debug_view_disasm>()->expression())); + m_inputEdit->selectAll(); + m_inputHistory.reset(); + return true; + } + else if (keyEvent.key() == Qt::Key_Up) + { + QString const *const hist = m_inputHistory.previous(m_inputEdit->text()); + if (hist) + { + m_inputEdit->setText(*hist); + m_inputEdit->setSelection(hist->size(), 0); + } + return true; + } + else if (keyEvent.key() == Qt::Key_Down) + { + QString const *const hist = m_inputHistory.next(m_inputEdit->text()); + if (hist) + { + m_inputEdit->setText(*hist); + m_inputEdit->setSelection(hist->size(), 0); + } + return true; + } + else + { + return QObject::eventFilter(obj, event); + } +} + + void DasmWindow::cpuChanged(int index) { if (index < m_dasmView->view()->source_count()) @@ -130,8 +239,18 @@ void DasmWindow::cpuChanged(int index) void DasmWindow::expressionSubmitted() { const QString expression = m_inputEdit->text(); - downcast<debug_view_disasm *>(m_dasmView->view())->set_expression(expression.toLocal8Bit().data()); - m_dasmView->viewport()->update(); + m_dasmView->view<debug_view_disasm>()->set_expression(expression.toUtf8().data()); + m_inputEdit->selectAll(); + + // Add history + if (!expression.isEmpty()) + m_inputHistory.add(expression); +} + + +void DasmWindow::expressionEdited(QString const &text) +{ + m_inputHistory.edit(); } @@ -139,7 +258,7 @@ void DasmWindow::toggleBreakpointAtCursor(bool changedTo) { if (m_dasmView->view()->cursor_visible()) { - offs_t const address = downcast<debug_view_disasm *>(m_dasmView->view())->selected_address(); + offs_t const address = m_dasmView->view<debug_view_disasm>()->selected_address(); device_t *const device = m_dasmView->view()->source()->device(); device_debug *const cpuinfo = device->debug(); @@ -149,7 +268,7 @@ void DasmWindow::toggleBreakpointAtCursor(bool changedTo) // If none exists, add a new one if (!bp) { - int32_t bpindex = cpuinfo->breakpoint_set(address, nullptr, nullptr); + int32_t bpindex = cpuinfo->breakpoint_set(address); m_machine.debugger().console().printf("Breakpoint %X set\n", bpindex); } else @@ -161,8 +280,6 @@ void DasmWindow::toggleBreakpointAtCursor(bool changedTo) m_machine.debug_view().update_all(); m_machine.debugger().refresh_display(); } - - refreshAll(); } @@ -170,7 +287,7 @@ void DasmWindow::enableBreakpointAtCursor(bool changedTo) { if (m_dasmView->view()->cursor_visible()) { - offs_t const address = downcast<debug_view_disasm *>(m_dasmView->view())->selected_address(); + offs_t const address = m_dasmView->view<debug_view_disasm>()->selected_address(); device_t *const device = m_dasmView->view()->source()->device(); device_debug *const cpuinfo = device->debug(); @@ -185,8 +302,6 @@ void DasmWindow::enableBreakpointAtCursor(bool changedTo) m_machine.debugger().refresh_display(); } } - - refreshAll(); } @@ -194,7 +309,7 @@ void DasmWindow::runToCursor(bool changedTo) { if (m_dasmView->view()->cursor_visible()) { - offs_t const address = downcast<debug_view_disasm *>(m_dasmView->view())->selected_address(); + offs_t const address = m_dasmView->view<debug_view_disasm>()->selected_address(); m_dasmView->view()->source()->device()->debug()->go(address); } } @@ -202,19 +317,8 @@ void DasmWindow::runToCursor(bool changedTo) void DasmWindow::rightBarChanged(QAction* changedTo) { - debug_view_disasm* dasmView = downcast<debug_view_disasm*>(m_dasmView->view()); - if (changedTo->text() == "Raw Opcodes") - { - dasmView->set_right_column(DASM_RIGHTCOL_RAW); - } - else if (changedTo->text() == "Encrypted Opcodes") - { - dasmView->set_right_column(DASM_RIGHTCOL_ENCRYPTED); - } - else if (changedTo->text() == "Comments") - { - dasmView->set_right_column(DASM_RIGHTCOL_COMMENTS); - } + debug_view_disasm *const dasmView = m_dasmView->view<debug_view_disasm>(); + dasmView->set_right_column(disasm_right_column(changedTo->data().toInt())); m_dasmView->viewport()->update(); } @@ -226,7 +330,7 @@ void DasmWindow::dasmViewUpdated() bool breakpointEnabled = false; if (haveCursor) { - offs_t const address = downcast<debug_view_disasm *>(m_dasmView->view())->selected_address(); + offs_t const address = m_dasmView->view<debug_view_disasm>()->selected_address(); device_t *const device = m_dasmView->view()->source()->device(); device_debug *const cpuinfo = device->debug(); @@ -275,47 +379,4 @@ void DasmWindow::setToCurrentCpu() } } - -//========================================================================= -// DasmWindowQtConfig -//========================================================================= -void DasmWindowQtConfig::buildFromQWidget(QWidget *widget) -{ - WindowQtConfig::buildFromQWidget(widget); - DasmWindow *window = dynamic_cast<DasmWindow *>(widget); - QComboBox *cpu = window->findChild<QComboBox *>("cpu"); - m_cpu = cpu->currentIndex(); - - QActionGroup *rightBarGroup = window->findChild<QActionGroup *>("rightbargroup"); - if (rightBarGroup->checkedAction()->text() == "Raw Opcodes") - m_rightBar = 0; - else if (rightBarGroup->checkedAction()->text() == "Encrypted Opcodes") - m_rightBar = 1; - else if (rightBarGroup->checkedAction()->text() == "Comments") - m_rightBar = 2; -} - -void DasmWindowQtConfig::applyToQWidget(QWidget *widget) -{ - WindowQtConfig::applyToQWidget(widget); - DasmWindow *window = dynamic_cast<DasmWindow *>(widget); - QComboBox *cpu = window->findChild<QComboBox *>("cpu"); - cpu->setCurrentIndex(m_cpu); - - QActionGroup *rightBarGroup = window->findChild<QActionGroup *>("rightbargroup"); - rightBarGroup->actions()[m_rightBar]->trigger(); -} - -void DasmWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const -{ - WindowQtConfig::addToXmlDataNode(node); - node.set_attribute_int("cpu", m_cpu); - node.set_attribute_int("rightbar", m_rightBar); -} - -void DasmWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); - m_cpu = node.get_attribute_int("cpu", m_cpu); - m_rightBar = node.get_attribute_int("rightbar", m_rightBar); -} +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/dasmwindow.h b/src/osd/modules/debugger/qt/dasmwindow.h index fc4ce11d731..df3fc0699a8 100644 --- a/src/osd/modules/debugger/qt/dasmwindow.h +++ b/src/osd/modules/debugger/qt/dasmwindow.h @@ -3,6 +3,8 @@ #ifndef MAME_DEBUGGER_QT_DASMWINDOW_H #define MAME_DEBUGGER_QT_DASMWINDOW_H +#pragma once + #include "debuggerview.h" #include "windowqt.h" @@ -10,6 +12,8 @@ #include <QtWidgets/QLineEdit> +namespace osd::debugger::qt { + //============================================================ // The Disassembly Window. //============================================================ @@ -18,12 +22,21 @@ class DasmWindow : public WindowQt Q_OBJECT public: - DasmWindow(running_machine &machine, QWidget *parent = nullptr); + DasmWindow(DebuggerQt &debugger, QWidget *parent = nullptr); virtual ~DasmWindow(); + virtual void restoreConfiguration(util::xml::data_node const &node) override; + +protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; + + // Used to intercept the user hitting the up arrow in the input widget + virtual bool eventFilter(QObject *obj, QEvent *event) override; + private slots: void cpuChanged(int index); void expressionSubmitted(); + void expressionEdited(QString const &text); void toggleBreakpointAtCursor(bool changedTo); void enableBreakpointAtCursor(bool changedTo); @@ -45,33 +58,11 @@ private: QAction *m_breakpointToggleAct; QAction *m_breakpointEnableAct; QAction *m_runToCursorAct; -}; - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class DasmWindowQtConfig : public WindowQtConfig -{ -public: - DasmWindowQtConfig() : - WindowQtConfig(WIN_TYPE_DASM), - m_cpu(0), - m_rightBar(0) - { - } - - ~DasmWindowQtConfig() {} - - // Settings - int m_cpu; - int m_rightBar; - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); + // Expression history + CommandHistory m_inputHistory; }; +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_DASMWINDOW_H diff --git a/src/osd/modules/debugger/qt/debuggerview.cpp b/src/osd/modules/debugger/qt/debuggerview.cpp index a1c8bcf2aec..84035c07ed4 100644 --- a/src/osd/modules/debugger/qt/debuggerview.cpp +++ b/src/osd/modules/debugger/qt/debuggerview.cpp @@ -3,8 +3,12 @@ #include "emu.h" #include "debuggerview.h" +#include "../xmlconfig.h" + #include "modules/lib/osdobj_common.h" +#include "xmlfile.h" + #include <QtCore/QMimeData> #include <QtGui/QClipboard> #include <QtGui/QKeyEvent> @@ -12,9 +16,8 @@ #include <QtWidgets/QApplication> #include <QtWidgets/QScrollBar> -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) -#define horizontalAdvance width -#endif + +namespace osd::debugger::qt { DebuggerView::DebuggerView( debug_view_type type, @@ -51,6 +54,19 @@ DebuggerView::~DebuggerView() m_machine.debug_view().free_view(*m_view); } + +int DebuggerView::sourceIndex() const +{ + if (m_view) + { + debug_view_source const *const source = m_view->source(); + if (source) + return m_view->source_index(*source); + } + return -1; +} + + void DebuggerView::paintEvent(QPaintEvent *event) { // Tell the MAME debug view how much real estate is available @@ -174,9 +190,43 @@ void DebuggerView::paintEvent(QPaintEvent *event) } } + +void DebuggerView::restoreConfigurationFromNode(util::xml::data_node const &node) +{ + if (m_view->cursor_supported()) + { + util::xml::data_node const *const selection = node.get_child(NODE_WINDOW_SELECTION); + if (selection) + { + debug_view_xy pos = m_view->cursor_position(); + m_view->set_cursor_visible(0 != selection->get_attribute_int(ATTR_SELECTION_CURSOR_VISIBLE, m_view->cursor_visible() ? 1 : 0)); + selection->get_attribute_int(ATTR_SELECTION_CURSOR_X, pos.x); + selection->get_attribute_int(ATTR_SELECTION_CURSOR_Y, pos.y); + m_view->set_cursor_position(pos); + } + } +} + + +void DebuggerView::saveConfigurationToNode(util::xml::data_node &node) +{ + if (m_view->cursor_supported()) + { + util::xml::data_node *const selection = node.add_child(NODE_WINDOW_SELECTION, nullptr); + if (selection) + { + debug_view_xy const pos = m_view->cursor_position(); + selection->set_attribute_int(ATTR_SELECTION_CURSOR_VISIBLE, m_view->cursor_visible() ? 1 : 0); + selection->set_attribute_int(ATTR_SELECTION_CURSOR_X, pos.x); + selection->set_attribute_int(ATTR_SELECTION_CURSOR_Y, pos.y); + } + } +} + + void DebuggerView::keyPressEvent(QKeyEvent* event) { - if (m_view == nullptr) + if (!m_view) return QWidget::keyPressEvent(event); Qt::KeyboardModifiers keyMods = QApplication::keyboardModifiers(); @@ -361,3 +411,5 @@ void DebuggerView::debuggerViewUpdate(debug_view &debugView, void *osdPrivate) dView->update(); emit dView->updated(); } + +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/debuggerview.h b/src/osd/modules/debugger/qt/debuggerview.h index a38b3b11152..cc68dd69d30 100644 --- a/src/osd/modules/debugger/qt/debuggerview.h +++ b/src/osd/modules/debugger/qt/debuggerview.h @@ -3,12 +3,16 @@ #ifndef MAME_DEBUGGER_QT_DEBUGGERVIEW_H #define MAME_DEBUGGER_QT_DEBUGGERVIEW_H +#pragma once + #include "debug/debugvw.h" #include <QtWidgets/QAbstractScrollArea> #include <QtWidgets/QMenu> +namespace osd::debugger::qt { + class DebuggerView : public QAbstractScrollArea { Q_OBJECT @@ -17,19 +21,24 @@ public: DebuggerView(debug_view_type type, running_machine &machine, QWidget *parent = nullptr); virtual ~DebuggerView(); - void paintEvent(QPaintEvent *event); + virtual void paintEvent(QPaintEvent *event) override; // Setters and accessors void setPreferBottom(bool pb) { m_preferBottom = pb; } debug_view *view() { return m_view; } + template <typename T> T *view() { return downcast<T *>(m_view); } + int sourceIndex() const; + + virtual void restoreConfigurationFromNode(util::xml::data_node const &node); + virtual void saveConfigurationToNode(util::xml::data_node &node); signals: void updated(); protected: - void keyPressEvent(QKeyEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void contextMenuEvent(QContextMenuEvent *event) override; + virtual void keyPressEvent(QKeyEvent *event) override; + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void contextMenuEvent(QContextMenuEvent *event) override; virtual void addItemsToContextMenu(QMenu *menu); @@ -49,4 +58,6 @@ private: bool m_preferBottom; }; +} // namespace osd::debugger::qt + #endif // MAME_DEBUGGER_QT_DEBUGGERVIEW_H diff --git a/src/osd/modules/debugger/qt/deviceinformationwindow.cpp b/src/osd/modules/debugger/qt/deviceinformationwindow.cpp index 431a0df492a..2e06d8fb1ed 100644 --- a/src/osd/modules/debugger/qt/deviceinformationwindow.cpp +++ b/src/osd/modules/debugger/qt/deviceinformationwindow.cpp @@ -1,15 +1,19 @@ // license:BSD-3-Clause // copyright-holders:Andrew Gardner #include "emu.h" +#include "deviceinformationwindow.h" + +#include "util/xmlfile.h" + #include <QtWidgets/QFrame> #include <QtWidgets/QLabel> #include <QtWidgets/QVBoxLayout> -#include "deviceinformationwindow.h" +namespace osd::debugger::qt { -DeviceInformationWindow::DeviceInformationWindow(running_machine &machine, device_t *device, QWidget *parent) : - WindowQt(machine, nullptr), +DeviceInformationWindow::DeviceInformationWindow(DebuggerQt &debugger, device_t *device, QWidget *parent) : + WindowQt(debugger, nullptr), m_device(device) { if (parent) @@ -27,6 +31,26 @@ DeviceInformationWindow::~DeviceInformationWindow() { } + +void DeviceInformationWindow::restoreConfiguration(util::xml::data_node const &node) +{ + WindowQt::restoreConfiguration(node); + + auto const tag = node.get_attribute_string(ATTR_WINDOW_DEVICE_TAG, ":"); + set_device(tag); +} + + +void DeviceInformationWindow::saveConfigurationToNode(util::xml::data_node &node) +{ + WindowQt::saveConfigurationToNode(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_DEVICE_INFO_VIEWER); + + node.set_attribute(ATTR_WINDOW_DEVICE_TAG, m_device->tag()); +} + + void DeviceInformationWindow::fill_device_information() { setWindowTitle(util::string_format("Debug: Device %s", m_device->tag()).c_str()); @@ -99,40 +123,4 @@ void DeviceInformationWindow::set_device(const char *tag) fill_device_information(); } -const char *DeviceInformationWindow::device_tag() const -{ - return m_device->tag(); -} - - -//========================================================================= -// DeviceInformationWindowQtConfig -//========================================================================= -void DeviceInformationWindowQtConfig::buildFromQWidget(QWidget *widget) -{ - WindowQtConfig::buildFromQWidget(widget); - DeviceInformationWindow *window = dynamic_cast<DeviceInformationWindow *>(widget); - m_device_tag = window->device_tag(); -} - - -void DeviceInformationWindowQtConfig::applyToQWidget(QWidget *widget) -{ - WindowQtConfig::applyToQWidget(widget); - DeviceInformationWindow *window = dynamic_cast<DeviceInformationWindow *>(widget); - window->set_device(m_device_tag.c_str()); -} - - -void DeviceInformationWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const -{ - WindowQtConfig::addToXmlDataNode(node); - node.set_attribute("device-tag", m_device_tag.c_str()); -} - - -void DeviceInformationWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); - m_device_tag = node.get_attribute_string("device-tag", ":"); -} +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/deviceinformationwindow.h b/src/osd/modules/debugger/qt/deviceinformationwindow.h index c7bfa68527d..e036a76e782 100644 --- a/src/osd/modules/debugger/qt/deviceinformationwindow.h +++ b/src/osd/modules/debugger/qt/deviceinformationwindow.h @@ -3,8 +3,13 @@ #ifndef MAME_DEBUGGER_QT_DEVICEINFORMATIONWINDOW_H #define MAME_DEBUGGER_QT_DEVICEINFORMATIONWINDOW_H +#pragma once + #include "windowqt.h" + +namespace osd::debugger::qt { + //============================================================ // The Device Information Window. //============================================================ @@ -13,11 +18,15 @@ class DeviceInformationWindow : public WindowQt Q_OBJECT public: - DeviceInformationWindow(running_machine &machine, device_t *device = nullptr, QWidget* parent=nullptr); + DeviceInformationWindow(DebuggerQt &debugger, device_t *device = nullptr, QWidget* parent=nullptr); virtual ~DeviceInformationWindow(); void set_device(const char *tag); - const char *device_tag() const; + + virtual void restoreConfiguration(util::xml::data_node const &node) override; + +protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; private: device_t *m_device; @@ -25,29 +34,6 @@ private: void fill_device_information(); }; - - - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class DeviceInformationWindowQtConfig : public WindowQtConfig -{ -public: - std::string m_device_tag; - - DeviceInformationWindowQtConfig() : - WindowQtConfig(WIN_TYPE_DEVICE_INFORMATION) - { - } - - ~DeviceInformationWindowQtConfig() {} - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); -}; - +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_DEVICEINFORMATIONWINDOW_H diff --git a/src/osd/modules/debugger/qt/deviceswindow.cpp b/src/osd/modules/debugger/qt/deviceswindow.cpp index 0e7a94ceb97..2c39c8cb0e8 100644 --- a/src/osd/modules/debugger/qt/deviceswindow.cpp +++ b/src/osd/modules/debugger/qt/deviceswindow.cpp @@ -2,8 +2,14 @@ // copyright-holders:Andrew Gardner #include "emu.h" #include "deviceswindow.h" + #include "deviceinformationwindow.h" +#include "util/xmlfile.h" + + +namespace osd::debugger::qt { + DevicesWindowModel::DevicesWindowModel(running_machine &machine, QObject *parent) : m_machine(machine) { @@ -107,9 +113,9 @@ int DevicesWindowModel::columnCount(const QModelIndex &parent) const -DevicesWindow::DevicesWindow(running_machine &machine, QWidget *parent) : - WindowQt(machine, nullptr), - m_devices_model(machine) +DevicesWindow::DevicesWindow(DebuggerQt &debugger, QWidget *parent) : + WindowQt(debugger, nullptr), + m_devices_model(debugger.machine()) { m_selected_device = nullptr; @@ -148,35 +154,15 @@ void DevicesWindow::currentRowChanged(const QModelIndex ¤t, const QModelIn void DevicesWindow::activated(const QModelIndex &index) { device_t *dev = static_cast<device_t *>(index.internalPointer()); - (new DeviceInformationWindow(m_machine, dev, this))->show(); + (new DeviceInformationWindow(m_debugger, dev, this))->show(); } - -//========================================================================= -// DevicesWindowQtConfig -//========================================================================= -void DevicesWindowQtConfig::buildFromQWidget(QWidget *widget) +void DevicesWindow::saveConfigurationToNode(util::xml::data_node &node) { - WindowQtConfig::buildFromQWidget(widget); - // DevicesWindow *window = dynamic_cast<DevicesWindow *>(widget); -} + WindowQt::saveConfigurationToNode(node); - -void DevicesWindowQtConfig::applyToQWidget(QWidget *widget) -{ - WindowQtConfig::applyToQWidget(widget); - // DevicesWindow *window = dynamic_cast<DevicesWindow *>(widget); + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_DEVICES_VIEWER); } - -void DevicesWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const -{ - WindowQtConfig::addToXmlDataNode(node); -} - - -void DevicesWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); -} +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/deviceswindow.h b/src/osd/modules/debugger/qt/deviceswindow.h index 884ad87d421..d337817fac0 100644 --- a/src/osd/modules/debugger/qt/deviceswindow.h +++ b/src/osd/modules/debugger/qt/deviceswindow.h @@ -3,11 +3,15 @@ #ifndef MAME_DEBUGGER_QT_DEVICESWINDOW_H #define MAME_DEBUGGER_QT_DEVICESWINDOW_H +#pragma once + #include "windowqt.h" #include <QtWidgets/QTreeView> +namespace osd::debugger::qt { + //============================================================ // The model for the treeview //============================================================ @@ -40,40 +44,22 @@ class DevicesWindow : public WindowQt Q_OBJECT public: - DevicesWindow(running_machine &machine, QWidget *parent = nullptr); + DevicesWindow(DebuggerQt &debugger, QWidget *parent = nullptr); virtual ~DevicesWindow(); public slots: void currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void activated(const QModelIndex &index); +protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; + private: QTreeView *m_devices_view; DevicesWindowModel m_devices_model; device_t *m_selected_device; }; - - - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class DevicesWindowQtConfig : public WindowQtConfig -{ -public: - DevicesWindowQtConfig() : - WindowQtConfig(WIN_TYPE_DEVICES) - { - } - - ~DevicesWindowQtConfig() {} - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); -}; - +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_DEVICESWINDOW_H diff --git a/src/osd/modules/debugger/qt/logwindow.cpp b/src/osd/modules/debugger/qt/logwindow.cpp index 9649014fc6f..dfbe947caca 100644 --- a/src/osd/modules/debugger/qt/logwindow.cpp +++ b/src/osd/modules/debugger/qt/logwindow.cpp @@ -1,17 +1,21 @@ // license:BSD-3-Clause // copyright-holders:Andrew Gardner #include "emu.h" -#include <QtWidgets/QVBoxLayout> - #include "logwindow.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debug/dvdisasm.h" +#include "util/xmlfile.h" + +#include <QtWidgets/QVBoxLayout> + + +namespace osd::debugger::qt { -LogWindow::LogWindow(running_machine &machine, QWidget *parent) : - WindowQt(machine, nullptr) +LogWindow::LogWindow(DebuggerQt &debugger, QWidget *parent) : + WindowQt(debugger, nullptr) { setWindowTitle("Debug: Machine Log"); @@ -44,28 +48,21 @@ LogWindow::~LogWindow() } -//========================================================================= -// LogWindowQtConfig -//========================================================================= -void LogWindowQtConfig::buildFromQWidget(QWidget *widget) +void LogWindow::restoreConfiguration(util::xml::data_node const &node) { - WindowQtConfig::buildFromQWidget(widget); -} - + WindowQt::restoreConfiguration(node); -void LogWindowQtConfig::applyToQWidget(QWidget *widget) -{ - WindowQtConfig::applyToQWidget(widget); + m_logView->restoreConfigurationFromNode(node); } -void LogWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const +void LogWindow::saveConfigurationToNode(util::xml::data_node &node) { - WindowQtConfig::addToXmlDataNode(node); -} + WindowQt::saveConfigurationToNode(node); + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_ERROR_LOG_VIEWER); -void LogWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); + m_logView->saveConfigurationToNode(node); } + +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/logwindow.h b/src/osd/modules/debugger/qt/logwindow.h index 05d4d15514f..f85d490ea75 100644 --- a/src/osd/modules/debugger/qt/logwindow.h +++ b/src/osd/modules/debugger/qt/logwindow.h @@ -3,10 +3,14 @@ #ifndef MAME_DEBUGGER_QT_LOGWINDOW_H #define MAME_DEBUGGER_QT_LOGWINDOW_H +#pragma once + #include "debuggerview.h" #include "windowqt.h" +namespace osd::debugger::qt { + //============================================================ // The Log Window. //============================================================ @@ -15,33 +19,19 @@ class LogWindow : public WindowQt Q_OBJECT public: - LogWindow(running_machine &machine, QWidget *parent = nullptr); + LogWindow(DebuggerQt &debugger, QWidget *parent = nullptr); virtual ~LogWindow(); + virtual void restoreConfiguration(util::xml::data_node const &node) override; + +protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; + private: // Widgets DebuggerView *m_logView; }; - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class LogWindowQtConfig : public WindowQtConfig -{ -public: - LogWindowQtConfig() : - WindowQtConfig(WIN_TYPE_LOG) - { - } - - ~LogWindowQtConfig() {} - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); -}; - +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_LOGWINDOW_H diff --git a/src/osd/modules/debugger/qt/mainwindow.cpp b/src/osd/modules/debugger/qt/mainwindow.cpp index afea2e07919..dba5d18b9ac 100644 --- a/src/osd/modules/debugger/qt/mainwindow.cpp +++ b/src/osd/modules/debugger/qt/mainwindow.cpp @@ -8,8 +8,15 @@ #include "debug/dvdisasm.h" #include "debug/points.h" +#include "util/xmlfile.h" + #include <QtGui/QCloseEvent> +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#else #include <QtWidgets/QAction> +#endif #include <QtWidgets/QDockWidget> #include <QtWidgets/QFileDialog> #include <QtWidgets/QMenu> @@ -17,10 +24,12 @@ #include <QtWidgets/QScrollBar> -MainWindow::MainWindow(running_machine &machine, QWidget *parent) : - WindowQt(machine, nullptr), - m_historyIndex(0), - m_inputHistory() +namespace osd::debugger::qt { + +MainWindow::MainWindow(DebuggerQt &debugger, QWidget *parent) : + WindowQt(debugger, nullptr), + m_inputHistory(), + m_exiting(false) { setGeometry(300, 300, 1000, 600); @@ -32,6 +41,7 @@ MainWindow::MainWindow(running_machine &machine, QWidget *parent) : // The input line m_inputEdit = new QLineEdit(mainWindowFrame); connect(m_inputEdit, &QLineEdit::returnPressed, this, &MainWindow::executeCommandSlot); + connect(m_inputEdit, &QLineEdit::textEdited, this, &MainWindow::commandEditedSlot); m_inputEdit->installEventFilter(this); @@ -56,7 +66,7 @@ MainWindow::MainWindow(running_machine &machine, QWidget *parent) : m_breakpointEnableAct = new QAction("Disable Breakpoint at Cursor", this); m_runToCursorAct = new QAction("Run to Cursor", this); m_breakpointToggleAct->setShortcut(Qt::Key_F9); - m_breakpointEnableAct->setShortcut(Qt::SHIFT + Qt::Key_F9); + m_breakpointEnableAct->setShortcut(Qt::SHIFT | Qt::Key_F9); m_runToCursorAct->setShortcut(Qt::Key_F4); connect(m_breakpointToggleAct, &QAction::triggered, this, &MainWindow::toggleBreakpointAtCursor); connect(m_breakpointEnableAct, &QAction::triggered, this, &MainWindow::enableBreakpointAtCursor); @@ -68,6 +78,9 @@ MainWindow::MainWindow(running_machine &machine, QWidget *parent) : QAction *rightActRaw = new QAction("Raw Opcodes", this); QAction *rightActEncrypted = new QAction("Encrypted Opcodes", this); QAction *rightActComments = new QAction("Comments", this); + rightActRaw->setData(int(DASM_RIGHTCOL_RAW)); + rightActEncrypted->setData(int(DASM_RIGHTCOL_ENCRYPTED)); + rightActComments->setData(int(DASM_RIGHTCOL_COMMENTS)); rightActRaw->setCheckable(true); rightActEncrypted->setCheckable(true); rightActComments->setCheckable(true); @@ -146,13 +159,54 @@ void MainWindow::setProcessor(device_t *processor) } +void MainWindow::restoreConfiguration(util::xml::data_node const &node) +{ + WindowQt::restoreConfiguration(node); + + debug_view_disasm &dasmview = *m_dasmFrame->view()->view<debug_view_disasm>(); + + restoreState(QByteArray::fromPercentEncoding(node.get_attribute_string("qtwindowstate", ""))); + + auto const rightbar = node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()); + QActionGroup *const rightBarGroup = findChild<QActionGroup *>("rightbargroup"); + for (QAction *action : rightBarGroup->actions()) + { + if (action->data().toInt() == rightbar) + { + action->trigger(); + break; + } + } + + m_dasmFrame->view()->restoreConfigurationFromNode(node); + m_inputHistory.restoreConfigurationFromNode(node); +} + + +void MainWindow::saveConfigurationToNode(util::xml::data_node &node) +{ + WindowQt::saveConfigurationToNode(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_CONSOLE); + + debug_view_disasm &dasmview = *m_dasmFrame->view()->view<debug_view_disasm>(); + node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()); + node.set_attribute("qtwindowstate", saveState().toPercentEncoding().data()); + + m_dasmFrame->view()->saveConfigurationToNode(node); + m_inputHistory.saveConfigurationToNode(node); +} + + // Used to intercept the user clicking 'X' in the upper corner void MainWindow::closeEvent(QCloseEvent *event) { - debugActQuit(); - - // Insure the window doesn't disappear before we get a chance to save its parameters - event->ignore(); + if (!m_exiting) + { + // Don't actually close the window - it will be brought back on user break + debugActRunAndHide(); + event->ignore(); + } } @@ -160,55 +214,56 @@ void MainWindow::closeEvent(QCloseEvent *event) bool MainWindow::eventFilter(QObject *obj, QEvent *event) { // Only filter keypresses - QKeyEvent *keyEvent = nullptr; - if (event->type() == QEvent::KeyPress) - keyEvent = static_cast<QKeyEvent*>(event); - else + if (event->type() != QEvent::KeyPress) return QObject::eventFilter(obj, event); + QKeyEvent const &keyEvent = *static_cast<QKeyEvent *>(event); + // Catch up & down keys - if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) + if (keyEvent.key() == Qt::Key_Escape) { - if (keyEvent->key() == Qt::Key_Up) - { - if (m_historyIndex > 0) - m_historyIndex--; - } - else if (keyEvent->key() == Qt::Key_Down) - { - if (m_historyIndex < m_inputHistory.size()) - m_historyIndex++; - } - - // Populate the input edit or clear it if you're at the end - if (m_historyIndex == m_inputHistory.size()) + m_inputEdit->clear(); + m_inputHistory.reset(); + return true; + } + else if (keyEvent.key() == Qt::Key_Up) + { + QString const *const hist = m_inputHistory.previous(m_inputEdit->text()); + if (hist) { - m_inputEdit->setText(""); + m_inputEdit->setText(*hist); + m_inputEdit->setSelection(hist->size(), 0); } - else + return true; + } + else if (keyEvent.key() == Qt::Key_Down) + { + QString const *const hist = m_inputHistory.next(m_inputEdit->text()); + if (hist) { - m_inputEdit->setText(m_inputHistory[m_historyIndex]); + m_inputEdit->setText(*hist); + m_inputEdit->setSelection(hist->size(), 0); } + return true; } - else if (keyEvent->key() == Qt::Key_Enter) + else if (keyEvent.key() == Qt::Key_Enter) { executeCommand(false); + return true; } else { return QObject::eventFilter(obj, event); } - - return true; } void MainWindow::toggleBreakpointAtCursor(bool changedTo) { - debug_view_disasm *const dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view()); + debug_view_disasm *const dasmView = m_dasmFrame->view()->view<debug_view_disasm>(); if (dasmView->cursor_visible() && (m_machine.debugger().console().get_visible_cpu() == dasmView->source()->device())) { - offs_t const address = downcast<debug_view_disasm *>(dasmView)->selected_address(); + offs_t const address = dasmView->selected_address(); device_debug *const cpuinfo = dasmView->source()->device()->debug(); // Find an existing breakpoint at this address @@ -221,15 +276,15 @@ void MainWindow::toggleBreakpointAtCursor(bool changedTo) else command = string_format("bpclear 0x%X", bp->index()); m_machine.debugger().console().execute_command(command, true); + m_machine.debug_view().update_all(); + m_machine.debugger().refresh_display(); } - - refreshAll(); } void MainWindow::enableBreakpointAtCursor(bool changedTo) { - debug_view_disasm *const dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view()); + debug_view_disasm *const dasmView = m_dasmFrame->view()->view<debug_view_disasm>(); if (dasmView->cursor_visible() && (m_machine.debugger().console().get_visible_cpu() == dasmView->source()->device())) { offs_t const address = dasmView->selected_address(); @@ -243,19 +298,19 @@ void MainWindow::enableBreakpointAtCursor(bool changedTo) int32_t const bpindex = bp->index(); std::string command = string_format(bp->enabled() ? "bpdisable 0x%X" : "bpenable 0x%X", bpindex); m_machine.debugger().console().execute_command(command, true); + m_machine.debug_view().update_all(); + m_machine.debugger().refresh_display(); } } - - refreshAll(); } void MainWindow::runToCursor(bool changedTo) { - debug_view_disasm *dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view()); + debug_view_disasm *const dasmView = m_dasmFrame->view()->view<debug_view_disasm>(); if (dasmView->cursor_visible() && (m_machine.debugger().console().get_visible_cpu() == dasmView->source()->device())) { - offs_t address = downcast<debug_view_disasm*>(dasmView)->selected_address(); + offs_t address = dasmView->selected_address(); std::string command = string_format("go 0x%X", address); m_machine.debugger().console().execute_command(command, true); } @@ -264,15 +319,8 @@ void MainWindow::runToCursor(bool changedTo) void MainWindow::rightBarChanged(QAction *changedTo) { - debug_view_disasm *dasmView = downcast<debug_view_disasm*>(m_dasmFrame->view()->view()); - - if (changedTo->text() == "Raw Opcodes") - dasmView->set_right_column(DASM_RIGHTCOL_RAW); - else if (changedTo->text() == "Encrypted Opcodes") - dasmView->set_right_column(DASM_RIGHTCOL_ENCRYPTED); - else if (changedTo->text() == "Comments") - dasmView->set_right_column(DASM_RIGHTCOL_COMMENTS); - + debug_view_disasm *const dasmView = m_dasmFrame->view()->view<debug_view_disasm>(); + dasmView->set_right_column(disasm_right_column(changedTo->data().toInt())); m_dasmFrame->view()->viewport()->update(); } @@ -281,33 +329,35 @@ void MainWindow::executeCommandSlot() executeCommand(true); } -void MainWindow::executeCommand(bool withClear) +void MainWindow::commandEditedSlot(QString const &text) { - QString command = m_inputEdit->text(); + m_inputHistory.edit(); +} - // A blank command is a "silent step" +void MainWindow::executeCommand(bool withClear) +{ + QString const command = m_inputEdit->text(); if (command == "") { + // A blank command is a "silent step" m_machine.debugger().console().get_visible_cpu()->debug()->single_step(); - return; + m_inputHistory.reset(); } + else + { + // Send along the command + m_machine.debugger().console().execute_command(command.toUtf8().data(), true); - // Send along the command - m_machine.debugger().console().execute_command(command.toLocal8Bit().data(), true); - - // Add history & set the index to be the top of the stack - addToHistory(command); + // Add history + m_inputHistory.add(command); - // Clear out the text and reset the history pointer only if asked - if (withClear) - { - m_inputEdit->clear(); - m_historyIndex = m_inputHistory.size(); + // Clear out the text and reset the history pointer only if asked + if (withClear) + { + m_inputEdit->clear(); + m_inputHistory.edit(); + } } - - // Refresh - m_consoleView->viewport()->update(); - refreshAll(); } @@ -320,7 +370,6 @@ void MainWindow::mountImage(bool changedTo) if (!img) { m_machine.debugger().console().printf("Something is wrong with the mount menu.\n"); - refreshAll(); return; } @@ -331,10 +380,10 @@ void MainWindow::mountImage(bool changedTo) QDir::currentPath(), tr("All files (*.*)")); - if (img->load(filename.toUtf8().data()) != image_init_result::PASS) + auto [err, message] = img->load(filename.toUtf8().data()); + if (err) { - m_machine.debugger().console().printf("Image could not be mounted.\n"); - refreshAll(); + m_machine.debugger().console().printf("Image could not be mounted: %s\n", !message.empty() ? message : err.message()); return; } @@ -350,7 +399,6 @@ void MainWindow::mountImage(bool changedTo) parentMenuItem->setTitle(newTitle); m_machine.debugger().console().printf("Image %s mounted successfully.\n", filename.toUtf8().data()); - refreshAll(); } @@ -374,13 +422,12 @@ void MainWindow::unmountImage(bool changedTo) parentMenuItem->setTitle(newTitle); m_machine.debugger().console().printf("Image successfully unmounted.\n"); - refreshAll(); } void MainWindow::dasmViewUpdated() { - debug_view_disasm *const dasmView = downcast<debug_view_disasm *>(m_dasmFrame->view()->view()); + debug_view_disasm *const dasmView = m_dasmFrame->view()->view<debug_view_disasm>(); bool const haveCursor = dasmView->cursor_visible() && (m_machine.debugger().console().get_visible_cpu() == dasmView->source()->device()); bool haveBreakpoint = false; bool breakpointEnabled = false; @@ -414,21 +461,10 @@ void MainWindow::debugActClose() } -void MainWindow::addToHistory(const QString& command) +void MainWindow::debuggerExit() { - if (command == "") - return; - - // Always push back when there is no previous history - if (m_inputHistory.empty()) - { - m_inputHistory.push_back(m_inputEdit->text()); - return; - } - - // If there is previous history, make sure it's not what you just executed - if (m_inputHistory.back() != m_inputEdit->text()) - m_inputHistory.push_back(m_inputEdit->text()); + m_exiting = true; + close(); } @@ -466,52 +502,6 @@ void MainWindow::createImagesMenu() } -//========================================================================= -// MainWindowQtConfig -//========================================================================= -void MainWindowQtConfig::buildFromQWidget(QWidget* widget) -{ - WindowQtConfig::buildFromQWidget(widget); - MainWindow *window = dynamic_cast<MainWindow *>(widget); - m_windowState = window->saveState(); - - QActionGroup *rightBarGroup = window->findChild<QActionGroup*>("rightbargroup"); - if (rightBarGroup->checkedAction()->text() == "Raw Opcodes") - m_rightBar = 0; - else if (rightBarGroup->checkedAction()->text() == "Encrypted Opcodes") - m_rightBar = 1; - else if (rightBarGroup->checkedAction()->text() == "Comments") - m_rightBar = 2; -} - - -void MainWindowQtConfig::applyToQWidget(QWidget *widget) -{ - WindowQtConfig::applyToQWidget(widget); - MainWindow *window = dynamic_cast<MainWindow *>(widget); - window->restoreState(m_windowState); - - QActionGroup* rightBarGroup = window->findChild<QActionGroup*>("rightbargroup"); - rightBarGroup->actions()[m_rightBar]->trigger(); -} - - -void MainWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const -{ - WindowQtConfig::addToXmlDataNode(node); - node.set_attribute_int("rightbar", m_rightBar); - node.set_attribute("qtwindowstate", m_windowState.toPercentEncoding().data()); -} - - -void MainWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); - const char* state = node.get_attribute_string("qtwindowstate", ""); - m_windowState = QByteArray::fromPercentEncoding(state); - m_rightBar = node.get_attribute_int("rightbar", m_rightBar); -} - DasmDockWidget::~DasmDockWidget() { } @@ -519,3 +509,5 @@ DasmDockWidget::~DasmDockWidget() ProcessorDockWidget::~ProcessorDockWidget() { } + +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/mainwindow.h b/src/osd/modules/debugger/qt/mainwindow.h index 6c1e9baefcb..762874b6aee 100644 --- a/src/osd/modules/debugger/qt/mainwindow.h +++ b/src/osd/modules/debugger/qt/mainwindow.h @@ -3,6 +3,8 @@ #ifndef MAME_DEBUGGER_QT_MAINWINDOW_H #define MAME_DEBUGGER_QT_MAINWINDOW_H +#pragma once + #include "debuggerview.h" #include "windowqt.h" @@ -12,8 +14,10 @@ #include <QtWidgets/QVBoxLayout> #include <QtWidgets/QComboBox> -#include <vector> +#include <deque> + +namespace osd::debugger::qt { class DasmDockWidget; class ProcessorDockWidget; @@ -27,18 +31,21 @@ class MainWindow : public WindowQt Q_OBJECT public: - MainWindow(running_machine &machine, QWidget *parent = nullptr); + MainWindow(DebuggerQt &debugger, QWidget *parent = nullptr); virtual ~MainWindow(); void setProcessor(device_t *processor); + virtual void restoreConfiguration(util::xml::data_node const &node) override; protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; + // Used to intercept the user clicking 'X' in the upper corner - void closeEvent(QCloseEvent *event); + virtual void closeEvent(QCloseEvent *event) override; // Used to intercept the user hitting the up arrow in the input widget - bool eventFilter(QObject *obj, QEvent *event); + virtual bool eventFilter(QObject *obj, QEvent *event) override; private slots: void toggleBreakpointAtCursor(bool changedTo); @@ -47,19 +54,22 @@ private slots: void rightBarChanged(QAction *changedTo); void executeCommandSlot(); + void commandEditedSlot(QString const &text); void mountImage(bool changedTo); void unmountImage(bool changedTo); void dasmViewUpdated(); - // Closing the main window actually exits the program - void debugActClose(); - + // Closing the main window hides the debugger and runs the emulated system + virtual void debugActClose() override; + virtual void debuggerExit() override; private: void createImagesMenu(); + void executeCommand(bool withClear); + // Widgets and docks QLineEdit *m_inputEdit; DebuggerView *m_consoleView; @@ -72,10 +82,9 @@ private: QAction *m_runToCursorAct; // Terminal history - int m_historyIndex; - std::vector<QString> m_inputHistory; - void addToHistory(const QString& command); - void executeCommand(bool withClear); + CommandHistory m_inputHistory; + + bool m_exiting; }; @@ -149,30 +158,6 @@ private: DebuggerView *m_processorView; }; - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class MainWindowQtConfig : public WindowQtConfig -{ -public: - MainWindowQtConfig() : - WindowQtConfig(WIN_TYPE_MAIN), - m_rightBar(0), - m_windowState() - {} - - ~MainWindowQtConfig() {} - - // Settings - int m_rightBar; - QByteArray m_windowState; - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); -}; - +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_MAINWINDOW_H diff --git a/src/osd/modules/debugger/qt/memorywindow.cpp b/src/osd/modules/debugger/qt/memorywindow.cpp index 09d13c5d6d4..f5e7502f4d4 100644 --- a/src/osd/modules/debugger/qt/memorywindow.cpp +++ b/src/osd/modules/debugger/qt/memorywindow.cpp @@ -7,9 +7,16 @@ #include "debug/debugcon.h" #include "debug/debugcpu.h" +#include "util/xmlfile.h" + #include <QtGui/QClipboard> +#include <QtGui/QKeyEvent> #include <QtGui/QMouseEvent> +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include <QtGui/QActionGroup> +#else #include <QtWidgets/QActionGroup> +#endif #include <QtWidgets/QApplication> #include <QtWidgets/QHBoxLayout> #include <QtWidgets/QMenu> @@ -18,12 +25,12 @@ #include <QtWidgets/QToolTip> #include <QtWidgets/QVBoxLayout> -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) -#define horizontalAdvance width -#endif -MemoryWindow::MemoryWindow(running_machine &machine, QWidget *parent) : - WindowQt(machine, nullptr) +namespace osd::debugger::qt { + +MemoryWindow::MemoryWindow(DebuggerQt &debugger, QWidget *parent) : + WindowQt(debugger, nullptr), + m_inputHistory() { setWindowTitle("Debug: Memory View"); @@ -44,6 +51,8 @@ MemoryWindow::MemoryWindow(running_machine &machine, QWidget *parent) : // The input edit m_inputEdit = new QLineEdit(topSubFrame); connect(m_inputEdit, &QLineEdit::returnPressed, this, &MemoryWindow::expressionSubmitted); + connect(m_inputEdit, &QLineEdit::textEdited, this, &MemoryWindow::expressionEdited); + m_inputEdit->installEventFilter(this); // The memory space combo box m_memoryComboBox = new QComboBox(topSubFrame); @@ -76,28 +85,28 @@ MemoryWindow::MemoryWindow(running_machine &machine, QWidget *parent) : // Create a data format group QActionGroup *dataFormat = new QActionGroup(this); dataFormat->setObjectName("dataformat"); - QAction *formatActOne = new QAction("1-byte hexadecimal", this); - QAction *formatActTwo = new QAction("2-byte hexadecimal", this); - QAction *formatActFour = new QAction("4-byte hexadecimal", this); - QAction *formatActEight = new QAction("8-byte hexadecimal", this); - QAction *formatActOneOctal = new QAction("1-byte octal", this); - QAction *formatActTwoOctal = new QAction("2-byte octal", this); - QAction *formatActFourOctal = new QAction("4-byte octal", this); - QAction *formatActEightOctal = new QAction("8-byte octal", this); - QAction *formatAct32bitFloat = new QAction("32 bit floating point", this); - QAction *formatAct64bitFloat = new QAction("64 bit floating point", this); - QAction *formatAct80bitFloat = new QAction("80 bit floating point", this); - formatActOne->setObjectName("formatActOne"); - formatActTwo->setObjectName("formatActTwo"); - formatActFour->setObjectName("formatActFour"); - formatActEight->setObjectName("formatActEight"); - formatActOneOctal->setObjectName("formatActOneOctal"); - formatActTwoOctal->setObjectName("formatActTwoOctal"); - formatActFourOctal->setObjectName("formatActFourOctal"); - formatActEightOctal->setObjectName("formatActEightOctal"); - formatAct32bitFloat->setObjectName("formatAct32bitFloat"); - formatAct64bitFloat->setObjectName("formatAct64bitFloat"); - formatAct80bitFloat->setObjectName("formatAct80bitFloat"); + QAction *formatActOne = new QAction("1-byte Chunks (Hex)", this); + QAction *formatActTwo = new QAction("2-byte Chunks (Hex)", this); + QAction *formatActFour = new QAction("4-byte Chunks (Hex)", this); + QAction *formatActEight = new QAction("8-byte Chunks (Hex)", this); + QAction *formatActOneOctal = new QAction("1-byte Chunks (Octal)", this); + QAction *formatActTwoOctal = new QAction("2-byte Chunks (Octal)", this); + QAction *formatActFourOctal = new QAction("4-byte Chunks (Octal)", this); + QAction *formatActEightOctal = new QAction("8-byte Chunks (Octal)", this); + QAction *formatAct32bitFloat = new QAction("32-bit Floating Point", this); + QAction *formatAct64bitFloat = new QAction("64-bit Floating Point", this); + QAction *formatAct80bitFloat = new QAction("80-bit Floating Point", this); + formatActOne->setData(int(debug_view_memory::data_format::HEX_8BIT)); + formatActTwo->setData(int(debug_view_memory::data_format::HEX_16BIT)); + formatActFour->setData(int(debug_view_memory::data_format::HEX_32BIT)); + formatActEight->setData(int(debug_view_memory::data_format::HEX_64BIT)); + formatActOneOctal->setData(int(debug_view_memory::data_format::OCTAL_8BIT)); + formatActTwoOctal->setData(int(debug_view_memory::data_format::OCTAL_16BIT)); + formatActFourOctal->setData(int(debug_view_memory::data_format::OCTAL_32BIT)); + formatActEightOctal->setData(int(debug_view_memory::data_format::OCTAL_64BIT)); + formatAct32bitFloat->setData(int(debug_view_memory::data_format::FLOAT_32BIT)); + formatAct64bitFloat->setData(int(debug_view_memory::data_format::FLOAT_64BIT)); + formatAct80bitFloat->setData(int(debug_view_memory::data_format::FLOAT_80BIT)); formatActOne->setCheckable(true); formatActTwo->setCheckable(true); formatActFour->setCheckable(true); @@ -139,6 +148,8 @@ MemoryWindow::MemoryWindow(running_machine &machine, QWidget *parent) : addressGroup->setObjectName("addressgroup"); QAction *addressActLogical = new QAction("Logical Addresses", this); QAction *addressActPhysical = new QAction("Physical Addresses", this); + addressActLogical->setData(false); + addressActPhysical->setData(true); addressActLogical->setCheckable(true); addressActPhysical->setCheckable(true); addressActLogical->setActionGroup(addressGroup); @@ -154,9 +165,9 @@ MemoryWindow::MemoryWindow(running_machine &machine, QWidget *parent) : QAction *radixActHexadecimal = new QAction("Hexadecimal Addresses", this); QAction *radixActDecimal = new QAction("Decimal Addresses", this); QAction *radixActOctal = new QAction("Octal Addresses", this); - radixActHexadecimal->setObjectName("radixHexadecimal"); - radixActDecimal->setObjectName("radixDecimal"); - radixActOctal->setObjectName("radixOctal"); + radixActHexadecimal->setData(16); + radixActDecimal->setData(10); + radixActOctal->setData(8); radixActHexadecimal->setCheckable(true); radixActDecimal->setCheckable(true); radixActOctal->setCheckable(true); @@ -212,6 +223,91 @@ MemoryWindow::~MemoryWindow() } +void MemoryWindow::restoreConfiguration(util::xml::data_node const &node) +{ + WindowQt::restoreConfiguration(node); + + debug_view_memory &memView = *m_memTable->view<debug_view_memory>(); + + auto const region = node.get_attribute_int(ATTR_WINDOW_MEMORY_REGION, m_memTable->sourceIndex()); + if ((0 <= region) && (m_memoryComboBox->count() > region)) + m_memoryComboBox->setCurrentIndex(region); + + auto const reverse = node.get_attribute_int(ATTR_WINDOW_MEMORY_REVERSE_COLUMNS, memView.reverse() ? 1 : 0); + if (memView.reverse() != bool(reverse)) + { + memView.set_reverse(bool(reverse)); + findChild<QAction *>("reverse")->setChecked(bool(reverse)); + } + + auto const mode = node.get_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_MODE, memView.physical() ? 1 : 0); + QActionGroup *const addressGroup = findChild<QActionGroup *>("addressgroup"); + for (QAction *action : addressGroup->actions()) + { + if (action->data().toBool() == mode) + { + action->trigger(); + break; + } + } + + auto const radix = node.get_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_RADIX, memView.address_radix()); + QActionGroup *const radixGroup = findChild<QActionGroup *>("radixgroup"); + for (QAction *action : radixGroup->actions()) + { + if (action->data().toInt() == radix) + { + action->trigger(); + break; + } + } + + auto const format = node.get_attribute_int(ATTR_WINDOW_MEMORY_DATA_FORMAT, int(memView.get_data_format())); + QActionGroup *const dataFormat = findChild<QActionGroup *>("dataformat"); + for (QAction *action : dataFormat->actions()) + { + if (action->data().toInt() == format) + { + action->trigger(); + break; + } + } + + auto const chunks = node.get_attribute_int(ATTR_WINDOW_MEMORY_ROW_CHUNKS, memView.chunks_per_row()); + memView.set_chunks_per_row(chunks); + + util::xml::data_node const *const expression = node.get_child(NODE_WINDOW_EXPRESSION); + if (expression && expression->get_value()) + { + m_inputEdit->setText(QString::fromUtf8(expression->get_value())); + expressionSubmitted(); + } + + m_memTable->restoreConfigurationFromNode(node); + m_inputHistory.restoreConfigurationFromNode(node); +} + + +void MemoryWindow::saveConfigurationToNode(util::xml::data_node &node) +{ + WindowQt::saveConfigurationToNode(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_MEMORY_VIEWER); + + debug_view_memory &memView = *m_memTable->view<debug_view_memory>(); + node.set_attribute_int(ATTR_WINDOW_MEMORY_REGION, m_memTable->sourceIndex()); + node.set_attribute_int(ATTR_WINDOW_MEMORY_REVERSE_COLUMNS, memView.reverse() ? 1 : 0); + node.set_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_MODE, memView.physical() ? 1 : 0); + node.set_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_RADIX, memView.address_radix()); + node.set_attribute_int(ATTR_WINDOW_MEMORY_DATA_FORMAT, int(memView.get_data_format())); + node.set_attribute_int(ATTR_WINDOW_MEMORY_ROW_CHUNKS, memView.chunks_per_row()); + node.add_child(NODE_WINDOW_EXPRESSION, memView.expression()); + + m_memTable->saveConfigurationToNode(node); + m_inputHistory.saveConfigurationToNode(node); +} + + void MemoryWindow::memoryRegionChanged(int index) { if (index < m_memTable->view()->source_count()) @@ -220,113 +316,120 @@ void MemoryWindow::memoryRegionChanged(int index) m_memTable->viewport()->update(); // Update the data format radio buttons to the memory region's default - debug_view_memory *memView = downcast<debug_view_memory*>(m_memTable->view()); - switch (memView->get_data_format()) + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); + + QActionGroup *const dataFormat = findChild<QActionGroup *>("dataformat"); + for (QAction *action : dataFormat->actions()) { - case debug_view_memory::data_format::HEX_8BIT: dataFormatMenuItem("formatActOne")->setChecked(true); break; - case debug_view_memory::data_format::HEX_16BIT: dataFormatMenuItem("formatActTwo")->setChecked(true); break; - case debug_view_memory::data_format::HEX_32BIT: dataFormatMenuItem("formatActFour")->setChecked(true); break; - case debug_view_memory::data_format::HEX_64BIT: dataFormatMenuItem("formatActEight")->setChecked(true); break; - case debug_view_memory::data_format::OCTAL_8BIT: dataFormatMenuItem("formatActOneOctal")->setChecked(true); break; - case debug_view_memory::data_format::OCTAL_16BIT: dataFormatMenuItem("formatActTwoOctal")->setChecked(true); break; - case debug_view_memory::data_format::OCTAL_32BIT: dataFormatMenuItem("formatActFourOctal")->setChecked(true); break; - case debug_view_memory::data_format::OCTAL_64BIT: dataFormatMenuItem("formatActEightOctal")->setChecked(true); break; - case debug_view_memory::data_format::FLOAT_32BIT: dataFormatMenuItem("formatAct32bitFloat")->setChecked(true); break; - case debug_view_memory::data_format::FLOAT_64BIT: dataFormatMenuItem("formatAct64bitFloat")->setChecked(true); break; - case debug_view_memory::data_format::FLOAT_80BIT: dataFormatMenuItem("formatAct80bitFloat")->setChecked(true); break; - default: break; + if (debug_view_memory::data_format(action->data().toInt()) == memView->get_data_format()) + { + action->setChecked(true); + break; + } } - switch (memView->address_radix()) + + QActionGroup *radixGroup = findChild<QActionGroup *>("radixgroup"); + for (QAction *action : radixGroup->actions()) { - case 8: dataFormatMenuItem("radixOctal")->setChecked(true); break; - case 10: dataFormatMenuItem("radixDecimal")->setChecked(true); break; - case 16: dataFormatMenuItem("radixHexadecimal")->setChecked(true); break; - default: break; + if (action->data().toInt() == memView->address_radix()) + { + action->setChecked(true); + break; + } } } } +// Used to intercept the user hitting the up arrow in the input widget +bool MemoryWindow::eventFilter(QObject *obj, QEvent *event) +{ + // Only filter keypresses + if (event->type() != QEvent::KeyPress) + return QObject::eventFilter(obj, event); + + QKeyEvent const &keyEvent = *static_cast<QKeyEvent *>(event); + + // Catch up & down keys + if (keyEvent.key() == Qt::Key_Escape) + { + m_inputEdit->setText(QString::fromUtf8(m_memTable->view<debug_view_memory>()->expression())); + m_inputEdit->selectAll(); + m_inputHistory.reset(); + return true; + } + else if (keyEvent.key() == Qt::Key_Up) + { + QString const *const hist = m_inputHistory.previous(m_inputEdit->text()); + if (hist) + { + m_inputEdit->setText(*hist); + m_inputEdit->setSelection(hist->size(), 0); + } + return true; + } + else if (keyEvent.key() == Qt::Key_Down) + { + QString const *const hist = m_inputHistory.next(m_inputEdit->text()); + if (hist) + { + m_inputEdit->setText(*hist); + m_inputEdit->setSelection(hist->size(), 0); + } + return true; + } + else + { + return QObject::eventFilter(obj, event); + } +} + + void MemoryWindow::expressionSubmitted() { const QString expression = m_inputEdit->text(); - downcast<debug_view_memory *>(m_memTable->view())->set_expression(expression.toLocal8Bit().data()); + m_memTable->view<debug_view_memory>()->set_expression(expression.toUtf8().data()); + m_inputEdit->selectAll(); - // Make the cursor pop - m_memTable->view()->set_cursor_visible(true); + // Add history + if (!expression.isEmpty()) + m_inputHistory.add(expression); +} - // Check where the cursor is and adjust the scroll accordingly - debug_view_xy cursorPosition = m_memTable->view()->cursor_position(); - // TODO: check if the region is already visible? - m_memTable->verticalScrollBar()->setValue(cursorPosition.y); - m_memTable->update(); - m_memTable->viewport()->update(); +void MemoryWindow::expressionEdited(QString const &text) +{ + m_inputHistory.edit(); } void MemoryWindow::formatChanged(QAction* changedTo) { - debug_view_memory *memView = downcast<debug_view_memory*>(m_memTable->view()); - - if (changedTo->text() == "1-byte hexadecimal") - memView->set_data_format(debug_view_memory::data_format::HEX_8BIT); - else if (changedTo->text() == "2-byte hexadecimal") - memView->set_data_format(debug_view_memory::data_format::HEX_16BIT); - else if (changedTo->text() == "4-byte hexadecimal") - memView->set_data_format(debug_view_memory::data_format::HEX_32BIT); - else if (changedTo->text() == "8-byte hexadecimal") - memView->set_data_format(debug_view_memory::data_format::HEX_64BIT); - else if (changedTo->text() == "1-byte octal") - memView->set_data_format(debug_view_memory::data_format::OCTAL_8BIT); - else if (changedTo->text() == "2-byte octal") - memView->set_data_format(debug_view_memory::data_format::OCTAL_16BIT); - else if (changedTo->text() == "4-byte octal") - memView->set_data_format(debug_view_memory::data_format::OCTAL_32BIT); - else if (changedTo->text() == "8-byte octal") - memView->set_data_format(debug_view_memory::data_format::OCTAL_64BIT); - else if (changedTo->text() == "32 bit floating point") - memView->set_data_format(debug_view_memory::data_format::FLOAT_32BIT); - else if (changedTo->text() == "64 bit floating point") - memView->set_data_format(debug_view_memory::data_format::FLOAT_64BIT); - else if (changedTo->text() == "80 bit floating point") - memView->set_data_format(debug_view_memory::data_format::FLOAT_80BIT); - + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); + memView->set_data_format(debug_view_memory::data_format(changedTo->data().toInt())); m_memTable->viewport()->update(); } void MemoryWindow::addressChanged(QAction* changedTo) { - debug_view_memory *memView = downcast<debug_view_memory *>(m_memTable->view()); - - if (changedTo->text() == "Logical Addresses") - memView->set_physical(false); - else if (changedTo->text() == "Physical Addresses") - memView->set_physical(true); - + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); + memView->set_physical(changedTo->data().toBool()); m_memTable->viewport()->update(); } void MemoryWindow::radixChanged(QAction* changedTo) { - debug_view_memory *memView = downcast<debug_view_memory *>(m_memTable->view()); - - if (changedTo->text() == "Hexadecimal Addresses") - memView->set_address_radix(16); - else if (changedTo->text() == "Decimal Addresses") - memView->set_address_radix(10); - else if (changedTo->text() == "Octal Addresses") - memView->set_address_radix(8); - + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); + memView->set_address_radix(changedTo->data().toInt()); m_memTable->viewport()->update(); } void MemoryWindow::reverseChanged(bool changedTo) { - debug_view_memory *memView = downcast<debug_view_memory*>(m_memTable->view()); + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); memView->set_reverse(changedTo); m_memTable->viewport()->update(); } @@ -334,7 +437,7 @@ void MemoryWindow::reverseChanged(bool changedTo) void MemoryWindow::increaseBytesPerLine(bool changedTo) { - debug_view_memory *memView = downcast<debug_view_memory*>(m_memTable->view()); + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); memView->set_chunks_per_row(memView->chunks_per_row() + 1); m_memTable->viewport()->update(); } @@ -342,7 +445,7 @@ void MemoryWindow::increaseBytesPerLine(bool changedTo) void MemoryWindow::decreaseBytesPerLine(bool checked) { - debug_view_memory *memView = downcast<debug_view_memory *>(m_memTable->view()); + debug_view_memory *const memView = m_memTable->view<debug_view_memory>(); memView->set_chunks_per_row(memView->chunks_per_row() - 1); m_memTable->viewport()->update(); } @@ -374,25 +477,6 @@ void MemoryWindow::setToCurrentCpu() } -// I have a hard time storing QActions as class members. This is a substitute. -QAction *MemoryWindow::dataFormatMenuItem(const QString& itemName) -{ - QList<QMenu *> menus = menuBar()->findChildren<QMenu *>(); - for (int i = 0; i < menus.length(); i++) - { - if (menus[i]->title() != "&Options") - continue; - QList<QAction *> actions = menus[i]->actions(); - for (int j = 0; j < actions.length(); j++) - { - if (actions[j]->objectName() == itemName) - return actions[j]; - } - } - return nullptr; -} - - //========================================================================= // DebuggerMemView //========================================================================= @@ -402,7 +486,7 @@ void DebuggerMemView::addItemsToContextMenu(QMenu *menu) if (view()->cursor_visible()) { - debug_view_memory &memView = downcast<debug_view_memory &>(*view()); + debug_view_memory &memView = *view<debug_view_memory>(); debug_view_memory_source const &source = downcast<debug_view_memory_source const &>(*memView.source()); address_space *const addressSpace = source.space(); if (addressSpace) @@ -412,29 +496,30 @@ void DebuggerMemView::addItemsToContextMenu(QMenu *menu) offs_t const address = addressSpace->byte_to_address(memView.addressAtCursorPosition(pos)); offs_t a = address & addressSpace->logaddrmask(); bool good = false; - if (!addressSpace->device().memory().translate(addressSpace->spacenum(), TRANSLATE_READ_DEBUG, a)) + address_space *tspace; + if (!addressSpace->device().memory().translate(addressSpace->spacenum(), device_memory_interface::TR_READ, a, tspace)) { m_lastPc = "Bad address"; } else { - uint64_t memValue = addressSpace->unmap(); - auto dis = addressSpace->device().machine().disable_side_effects(); - switch (addressSpace->data_width()) + uint64_t memValue = tspace->unmap(); + auto dis = tspace->device().machine().disable_side_effects(); + switch (tspace->data_width()) { - case 8: memValue = addressSpace->read_byte(a); break; - case 16: memValue = addressSpace->read_word_unaligned(a); break; - case 32: memValue = addressSpace->read_dword_unaligned(a); break; - case 64: memValue = addressSpace->read_qword_unaligned(a); break; + case 8: memValue = tspace->read_byte(a); break; + case 16: memValue = tspace->read_word_unaligned(a); break; + case 32: memValue = tspace->read_dword_unaligned(a); break; + case 64: memValue = tspace->read_qword_unaligned(a); break; } offs_t const pc = source.device()->debug()->track_mem_pc_from_space_address_data( - addressSpace->spacenum(), + tspace->spacenum(), address, memValue); if (pc != offs_t(-1)) { - if (addressSpace->is_octal()) + if (tspace->is_octal()) m_lastPc = QString("Address %1 written at PC=%2").arg(address, 2, 8).arg(pc, 2, 8); else m_lastPc = QString("Address %1 written at PC=%2").arg(address, 2, 16).arg(pc, 2, 16); @@ -462,99 +547,4 @@ void DebuggerMemView::copyLastPc() QApplication::clipboard()->setText(m_lastPc); } - -//========================================================================= -// MemoryWindowQtConfig -//========================================================================= -void MemoryWindowQtConfig::buildFromQWidget(QWidget *widget) -{ - WindowQtConfig::buildFromQWidget(widget); - MemoryWindow *window = dynamic_cast<MemoryWindow *>(widget); - QComboBox *memoryRegion = window->findChild<QComboBox*>("memoryregion"); - m_memoryRegion = memoryRegion->currentIndex(); - - QAction *reverse = window->findChild<QAction *>("reverse"); - m_reverse = reverse->isChecked(); - - QActionGroup *addressGroup = window->findChild<QActionGroup*>("addressgroup"); - if (addressGroup->checkedAction()->text() == "Logical Addresses") - m_addressMode = 0; - else if (addressGroup->checkedAction()->text() == "Physical Addresses") - m_addressMode = 1; - - QActionGroup *radixGroup = window->findChild<QActionGroup*>("radixgroup"); - if (radixGroup->checkedAction()->text() == "Hexadecimal Addresses") - m_addressRadix = 0; - else if (radixGroup->checkedAction()->text() == "Decimal Addresses") - m_addressRadix = 1; - else if (radixGroup->checkedAction()->text() == "Octal Addresses") - m_addressRadix = 2; - - QActionGroup *dataFormat = window->findChild<QActionGroup*>("dataformat"); - if (dataFormat->checkedAction()->text() == "1-byte hexadecimal") - m_dataFormat = 0; - else if (dataFormat->checkedAction()->text() == "2-byte hexadecimal") - m_dataFormat = 1; - else if (dataFormat->checkedAction()->text() == "4-byte hexadecimal") - m_dataFormat = 2; - else if (dataFormat->checkedAction()->text() == "8-byte hexadecimal") - m_dataFormat = 3; - else if (dataFormat->checkedAction()->text() == "1-byte octal") - m_dataFormat = 4; - else if (dataFormat->checkedAction()->text() == "2-byte octal") - m_dataFormat = 5; - else if (dataFormat->checkedAction()->text() == "4-byte octal") - m_dataFormat = 6; - else if (dataFormat->checkedAction()->text() == "8-byte octal") - m_dataFormat = 7; - else if (dataFormat->checkedAction()->text() == "32 bit floating point") - m_dataFormat = 8; - else if (dataFormat->checkedAction()->text() == "64 bit floating point") - m_dataFormat = 9; - else if (dataFormat->checkedAction()->text() == "80 bit floating point") - m_dataFormat = 10; -} - - -void MemoryWindowQtConfig::applyToQWidget(QWidget *widget) -{ - WindowQtConfig::applyToQWidget(widget); - MemoryWindow *window = dynamic_cast<MemoryWindow *>(widget); - QComboBox *memoryRegion = window->findChild<QComboBox *>("memoryregion"); - memoryRegion->setCurrentIndex(m_memoryRegion); - - QAction *reverse = window->findChild<QAction *>("reverse"); - if (m_reverse) - reverse->trigger(); - - QActionGroup *addressGroup = window->findChild<QActionGroup*>("addressgroup"); - addressGroup->actions()[m_addressMode]->trigger(); - - QActionGroup *radixGroup = window->findChild<QActionGroup*>("radixgroup"); - radixGroup->actions()[m_addressRadix]->trigger(); - - QActionGroup *dataFormat = window->findChild<QActionGroup*>("dataformat"); - dataFormat->actions()[m_dataFormat]->trigger(); -} - - -void MemoryWindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const -{ - WindowQtConfig::addToXmlDataNode(node); - node.set_attribute_int("memoryregion", m_memoryRegion); - node.set_attribute_int("reverse", m_reverse); - node.set_attribute_int("addressmode", m_addressMode); - node.set_attribute_int("addressradix", m_addressRadix); - node.set_attribute_int("dataformat", m_dataFormat); -} - - -void MemoryWindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) -{ - WindowQtConfig::recoverFromXmlNode(node); - m_memoryRegion = node.get_attribute_int("memoryregion", m_memoryRegion); - m_reverse = node.get_attribute_int("reverse", m_reverse); - m_addressMode = node.get_attribute_int("addressmode", m_addressMode); - m_addressRadix = node.get_attribute_int("addressradix", m_addressRadix); - m_dataFormat = node.get_attribute_int("dataformat", m_dataFormat); -} +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/memorywindow.h b/src/osd/modules/debugger/qt/memorywindow.h index 86193cc3edc..ed5c5b30cb0 100644 --- a/src/osd/modules/debugger/qt/memorywindow.h +++ b/src/osd/modules/debugger/qt/memorywindow.h @@ -3,12 +3,17 @@ #ifndef MAME_DEBUGGER_QT_MEMORYWINDOW_H #define MAME_DEBUGGER_QT_MEMORYWINDOW_H +#pragma once + #include "debuggerview.h" #include "windowqt.h" #include <QtWidgets/QComboBox> #include <QtWidgets/QLineEdit> + +namespace osd::debugger::qt { + class DebuggerMemView; @@ -20,12 +25,22 @@ class MemoryWindow : public WindowQt Q_OBJECT public: - MemoryWindow(running_machine &machine, QWidget *parent = nullptr); + MemoryWindow(DebuggerQt &debugger, QWidget *parent = nullptr); virtual ~MemoryWindow(); + virtual void restoreConfiguration(util::xml::data_node const &node) override; + +protected: + virtual void saveConfigurationToNode(util::xml::data_node &node) override; + + // Used to intercept the user hitting the up arrow in the input widget + virtual bool eventFilter(QObject *obj, QEvent *event) override; + private slots: void memoryRegionChanged(int index); void expressionSubmitted(); + void expressionEdited(QString const &text); + void formatChanged(QAction *changedTo); void addressChanged(QAction *changedTo); void radixChanged(QAction *changedTo); @@ -36,12 +51,14 @@ private slots: private: void populateComboBox(); void setToCurrentCpu(); - QAction *dataFormatMenuItem(const QString &itemName); // Widgets QLineEdit *m_inputEdit; QComboBox *m_memoryComboBox; DebuggerMemView *m_memTable; + + // Expression history + CommandHistory m_inputHistory; }; @@ -69,37 +86,6 @@ private: QString m_lastPc; }; - -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class MemoryWindowQtConfig : public WindowQtConfig -{ -public: - MemoryWindowQtConfig() : - WindowQtConfig(WIN_TYPE_MEMORY), - m_reverse(0), - m_addressMode(0), - m_addressRadix(0), - m_dataFormat(0), - m_memoryRegion(0) - { - } - - ~MemoryWindowQtConfig() {} - - // Settings - int m_reverse; - int m_addressMode; - int m_addressRadix; - int m_dataFormat; - int m_memoryRegion; - - void buildFromQWidget(QWidget *widget); - void applyToQWidget(QWidget *widget); - void addToXmlDataNode(util::xml::data_node &node) const; - void recoverFromXmlNode(util::xml::data_node const &node); -}; - +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_MEMORYWINDOW_H diff --git a/src/osd/modules/debugger/qt/windowqt.cpp b/src/osd/modules/debugger/qt/windowqt.cpp index 852d1f5449c..783c5a24393 100644 --- a/src/osd/modules/debugger/qt/windowqt.cpp +++ b/src/osd/modules/debugger/qt/windowqt.cpp @@ -12,38 +12,46 @@ #include "debug/debugcon.h" #include "debug/debugcpu.h" +#include "util/xmlfile.h" + #include <QtWidgets/QMenu> #include <QtWidgets/QMenuBar> -bool WindowQt::s_refreshAll = false; -bool WindowQt::s_hideAll = false; +namespace osd::debugger::qt { // Since all debug windows are intended to be top-level, this inherited // constructor is always called with a nullptr parent. The passed-in parent widget, // however, is often used to place each child window & the code to do this can // be found in most of the inherited classes. -WindowQt::WindowQt(running_machine &machine, QWidget *parent) : +WindowQt::WindowQt(DebuggerQt &debugger, QWidget *parent) : QMainWindow(parent), - m_machine(machine) + m_debugger(debugger), + m_machine(debugger.machine()) { setAttribute(Qt::WA_DeleteOnClose, true); + // Subscribe to signals + connect(&debugger, &DebuggerQt::exitDebugger, this, &WindowQt::debuggerExit); + connect(&debugger, &DebuggerQt::hideAllWindows, this, &WindowQt::hide); + connect(&debugger, &DebuggerQt::showAllWindows, this, &WindowQt::show); + connect(&debugger, &DebuggerQt::saveConfiguration, this, &WindowQt::saveConfiguration); + // The Debug menu bar QAction *debugActOpenMemory = new QAction("New &Memory Window", this); debugActOpenMemory->setShortcut(QKeySequence("Ctrl+M")); connect(debugActOpenMemory, &QAction::triggered, this, &WindowQt::debugActOpenMemory); - QAction *debugActOpenDasm = new QAction("New &Dasm Window", this); + QAction *debugActOpenDasm = new QAction("New &Disassembly Window", this); debugActOpenDasm->setShortcut(QKeySequence("Ctrl+D")); connect(debugActOpenDasm, &QAction::triggered, this, &WindowQt::debugActOpenDasm); - QAction *debugActOpenLog = new QAction("New &Log Window", this); + QAction *debugActOpenLog = new QAction("New Error &Log Window", this); debugActOpenLog->setShortcut(QKeySequence("Ctrl+L")); connect(debugActOpenLog, &QAction::triggered, this, &WindowQt::debugActOpenLog); - QAction *debugActOpenPoints = new QAction("New &Break|Watchpoints Window", this); + QAction *debugActOpenPoints = new QAction("New (&Break|Watch)points Window", this); debugActOpenPoints->setShortcut(QKeySequence("Ctrl+B")); connect(debugActOpenPoints, &QAction::triggered, this, &WindowQt::debugActOpenPoints); @@ -128,9 +136,10 @@ WindowQt::~WindowQt() { } + void WindowQt::debugActOpenMemory() { - MemoryWindow *foo = new MemoryWindow(m_machine, this); + MemoryWindow *foo = new MemoryWindow(m_debugger, this); // A valiant effort, but it just doesn't wanna' hide behind the main window & not make a new toolbar icon // foo->setWindowFlags(Qt::Dialog); // foo->setWindowFlags(foo->windowFlags() & ~Qt::WindowStaysOnTopHint); @@ -140,7 +149,7 @@ void WindowQt::debugActOpenMemory() void WindowQt::debugActOpenDasm() { - DasmWindow *foo = new DasmWindow(m_machine, this); + DasmWindow *foo = new DasmWindow(m_debugger, this); // A valiant effort, but it just doesn't wanna' hide behind the main window & not make a new toolbar icon // foo->setWindowFlags(Qt::Dialog); // foo->setWindowFlags(foo->windowFlags() & ~Qt::WindowStaysOnTopHint); @@ -150,7 +159,7 @@ void WindowQt::debugActOpenDasm() void WindowQt::debugActOpenLog() { - LogWindow *foo = new LogWindow(m_machine, this); + LogWindow *foo = new LogWindow(m_debugger, this); // A valiant effort, but it just doesn't wanna' hide behind the main window & not make a new toolbar icon // foo->setWindowFlags(Qt::Dialog); // foo->setWindowFlags(foo->windowFlags() & ~Qt::WindowStaysOnTopHint); @@ -160,7 +169,7 @@ void WindowQt::debugActOpenLog() void WindowQt::debugActOpenPoints() { - BreakpointsWindow *foo = new BreakpointsWindow(m_machine, this); + BreakpointsWindow *foo = new BreakpointsWindow(m_debugger, this); // A valiant effort, but it just doesn't wanna' hide behind the main window & not make a new toolbar icon // foo->setWindowFlags(Qt::Dialog); // foo->setWindowFlags(foo->windowFlags() & ~Qt::WindowStaysOnTopHint); @@ -170,7 +179,7 @@ void WindowQt::debugActOpenPoints() void WindowQt::debugActOpenDevices() { - DevicesWindow *foo = new DevicesWindow(m_machine, this); + DevicesWindow *foo = new DevicesWindow(m_debugger, this); // A valiant effort, but it just doesn't wanna' hide behind the main window & not make a new toolbar icon // foo->setWindowFlags(Qt::Dialog); // foo->setWindowFlags(foo->windowFlags() & ~Qt::WindowStaysOnTopHint); @@ -186,7 +195,7 @@ void WindowQt::debugActRun() void WindowQt::debugActRunAndHide() { m_machine.debugger().console().get_visible_cpu()->debug()->go(); - hideAll(); + m_debugger.hideAll(); } void WindowQt::debugActRunToNextCpu() @@ -240,40 +249,154 @@ void WindowQt::debugActQuit() m_machine.schedule_exit(); } +void WindowQt::debuggerExit() +{ + close(); +} + + +void WindowQt::restoreConfiguration(util::xml::data_node const &node) +{ + QPoint p(geometry().topLeft()); + p.setX(node.get_attribute_int(ATTR_WINDOW_POSITION_X, p.x())); + p.setY(node.get_attribute_int(ATTR_WINDOW_POSITION_Y, p.y())); + + QSize s(size()); + s.setWidth(node.get_attribute_int(ATTR_WINDOW_WIDTH, s.width())); + s.setHeight(node.get_attribute_int(ATTR_WINDOW_HEIGHT, s.height())); + + // TODO: sanity checks, restrict to screen area + + setGeometry(p.x(), p.y(), s.width(), s.height()); +} + + +void WindowQt::saveConfiguration(util::xml::data_node &parentnode) +{ + util::xml::data_node *const node = parentnode.add_child(NODE_WINDOW, nullptr); + if (node) + saveConfigurationToNode(*node); +} + -//========================================================================= -// WindowQtConfig -//========================================================================= -void WindowQtConfig::buildFromQWidget(QWidget *widget) +void WindowQt::saveConfigurationToNode(util::xml::data_node &node) { - m_position.setX(widget->geometry().topLeft().x()); - m_position.setY(widget->geometry().topLeft().y()); - m_size.setX(widget->size().width()); - m_size.setY(widget->size().height()); + node.set_attribute_int(ATTR_WINDOW_POSITION_X, geometry().topLeft().x()); + node.set_attribute_int(ATTR_WINDOW_POSITION_Y, geometry().topLeft().y()); + node.set_attribute_int(ATTR_WINDOW_WIDTH, size().width()); + node.set_attribute_int(ATTR_WINDOW_HEIGHT, size().height()); } -void WindowQtConfig::applyToQWidget(QWidget *widget) +CommandHistory::CommandHistory() : + m_history(), + m_current(), + m_position(-1) { - widget->setGeometry(m_position.x(), m_position.y(), m_size.x(), m_size.y()); } -void WindowQtConfig::addToXmlDataNode(util::xml::data_node &node) const +CommandHistory::~CommandHistory() { - node.set_attribute_int("type", m_type); - node.set_attribute_int("position_x", m_position.x()); - node.set_attribute_int("position_y", m_position.y()); - node.set_attribute_int("size_x", m_size.x()); - node.set_attribute_int("size_y", m_size.y()); } -void WindowQtConfig::recoverFromXmlNode(util::xml::data_node const &node) +void CommandHistory::add(QString const &entry) { - m_size.setX(node.get_attribute_int("size_x", m_size.x())); - m_size.setY(node.get_attribute_int("size_y", m_size.y())); - m_position.setX(node.get_attribute_int("position_x", m_position.x())); - m_position.setY(node.get_attribute_int("position_y", m_position.y())); - m_type = (WindowQtConfig::WindowType)node.get_attribute_int("type", m_type); + if (m_history.empty() || (m_history.front() != entry)) + { + while (m_history.size() >= CAPACITY) + m_history.pop_back(); + m_history.push_front(entry); + } + m_position = 0; } + + +QString const *CommandHistory::previous(QString const ¤t) +{ + if ((m_position + 1) < m_history.size()) + { + if (0 > m_position) + m_current = std::make_unique<QString>(current); + return &m_history[++m_position]; + } + else + { + return nullptr; + } +} + + +QString const *CommandHistory::next(QString const ¤t) +{ + if (0 < m_position) + { + return &m_history[--m_position]; + } + else if (!m_position && m_current && (m_history.front() != *m_current)) + { + --m_position; + return m_current.get(); + } + else + { + return nullptr; + } +} + + +void CommandHistory::edit() +{ + if (!m_position) + --m_position; +} + + +void CommandHistory::reset() +{ + m_position = -1; + m_current.reset(); +} + + +void CommandHistory::clear() +{ + m_position = -1; + m_current.reset(); + m_history.clear(); +} + + +void CommandHistory::restoreConfigurationFromNode(util::xml::data_node const &node) +{ + clear(); + util::xml::data_node const *const historynode = node.get_child(NODE_WINDOW_HISTORY); + if (historynode) + { + util::xml::data_node const *itemnode = historynode->get_child(NODE_HISTORY_ITEM); + while (itemnode) + { + if (itemnode->get_value() && *itemnode->get_value()) + { + while (m_history.size() >= CAPACITY) + m_history.pop_back(); + m_history.push_front(QString::fromUtf8(itemnode->get_value())); + } + itemnode = itemnode->get_next_sibling(NODE_HISTORY_ITEM); + } + } +} + + +void CommandHistory::saveConfigurationToNode(util::xml::data_node &node) +{ + util::xml::data_node *const historynode = node.add_child(NODE_WINDOW_HISTORY, nullptr); + if (historynode) + { + for (auto it = m_history.crbegin(); m_history.crend() != it; ++it) + historynode->add_child(NODE_HISTORY_ITEM, it->toUtf8().data()); + } +} + +} // namespace osd::debugger::qt diff --git a/src/osd/modules/debugger/qt/windowqt.h b/src/osd/modules/debugger/qt/windowqt.h index 15cbe470f2f..8085f9a63bf 100644 --- a/src/osd/modules/debugger/qt/windowqt.h +++ b/src/osd/modules/debugger/qt/windowqt.h @@ -3,11 +3,39 @@ #ifndef MAME_DEBUGGER_QT_WINDOWQT_H #define MAME_DEBUGGER_QT_WINDOWQT_H -#include "config.h" +#include "../xmlconfig.h" + #include "debugger.h" #include <QtWidgets/QMainWindow> +#include <deque> +#include <memory> + + +namespace osd::debugger::qt { + +//============================================================ +// The Qt debugger module interface +//============================================================ +class DebuggerQt : public QObject +{ + Q_OBJECT + +public: + virtual ~DebuggerQt() { } + + virtual running_machine &machine() const = 0; + + void hideAll() { emit hideAllWindows(); } + +signals: + void exitDebugger(); + void hideAllWindows(); + void showAllWindows(); + void saveConfiguration(util::xml::data_node &parentnode); +}; + //============================================================ // The Qt window that everyone derives from. @@ -17,18 +45,9 @@ class WindowQt : public QMainWindow Q_OBJECT public: - WindowQt(running_machine &machine, QWidget *parent = nullptr); virtual ~WindowQt(); - // The interface to an all-window refresh - void refreshAll() { s_refreshAll = true; } - bool wantsRefresh() { return s_refreshAll; } - void clearRefreshFlag() { s_refreshAll = false; } - - void hideAll() { s_hideAll = true; } - bool wantsHide() { return s_hideAll; } - void clearHideFlag() { s_hideAll = false; } - + virtual void restoreConfiguration(util::xml::data_node const &node); protected slots: void debugActOpenMemory(); @@ -48,52 +67,48 @@ protected slots: void debugActHardReset(); virtual void debugActClose(); void debugActQuit(); + virtual void debuggerExit(); +private slots: + void saveConfiguration(util::xml::data_node &parentnode); protected: - running_machine &m_machine; + WindowQt(DebuggerQt &debugger, QWidget *parent = nullptr); + + virtual void saveConfigurationToNode(util::xml::data_node &node); - static bool s_refreshAll; - static bool s_hideAll; + DebuggerQt &m_debugger; + running_machine &m_machine; }; -//========================================================================= -// A way to store the configuration of a window long enough to read/write. -//========================================================================= -class WindowQtConfig +//============================================================ +// Command history helper +//============================================================ +class CommandHistory { public: - enum WindowType - { - WIN_TYPE_UNKNOWN, - WIN_TYPE_MAIN, - WIN_TYPE_MEMORY, - WIN_TYPE_DASM, - WIN_TYPE_LOG, - WIN_TYPE_BREAK_POINTS, - WIN_TYPE_DEVICES, - WIN_TYPE_DEVICE_INFORMATION - }; + CommandHistory(); + ~CommandHistory(); -public: - WindowQtConfig(const WindowType& type=WIN_TYPE_UNKNOWN) : - m_type(type), - m_size(800, 600), - m_position(120, 120) - {} - virtual ~WindowQtConfig() {} - - // Settings - WindowType m_type; - QPoint m_size; - QPoint m_position; - - virtual void buildFromQWidget(QWidget *widget); - virtual void applyToQWidget(QWidget *widget); - virtual void addToXmlDataNode(util::xml::data_node &node) const; - virtual void recoverFromXmlNode(util::xml::data_node const &node); + void add(QString const &entry); + QString const *previous(QString const ¤t); + QString const *next(QString const ¤t); + void edit(); + void reset(); + void clear(); + + void restoreConfigurationFromNode(util::xml::data_node const &node); + void saveConfigurationToNode(util::xml::data_node &node); + +private: + static inline constexpr unsigned CAPACITY = 100U; + + std::deque<QString> m_history; + std::unique_ptr<QString> m_current; + int m_position; }; +} // namespace osd::debugger::qt #endif // MAME_DEBUGGER_QT_WINDOWQT_H diff --git a/src/osd/modules/debugger/win/consolewininfo.cpp b/src/osd/modules/debugger/win/consolewininfo.cpp index b2f12b69331..3a4a52be2e2 100644 --- a/src/osd/modules/debugger/win/consolewininfo.cpp +++ b/src/osd/modules/debugger/win/consolewininfo.cpp @@ -12,6 +12,9 @@ #include "debugviewinfo.h" #include "uimetrics.h" +// devices +#include "imagedev/cassette.h" + // emu #include "debug/debugcon.h" #include "debugger.h" @@ -19,8 +22,8 @@ #include "softlist_dev.h" #include "debug/debugcpu.h" -// devices -#include "imagedev/cassette.h" +// util +#include "util/xmlfile.h" // osd/windows #include "winutf8.h" @@ -39,6 +42,8 @@ #include <wrl/client.h> +namespace osd::debugger::win { + namespace { class comdlg_filter_helper @@ -121,7 +126,7 @@ void choose_image(device_image_interface &device, HWND owner, REFCLSID class_id, // create file dialog Microsoft::WRL::ComPtr<IFileDialog> dialog; - hr = CoCreateInstance(class_id, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(dialog.GetAddressOf())); + hr = CoCreateInstance(class_id, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog)); // set file types if (SUCCEEDED(hr)) @@ -154,7 +159,7 @@ void choose_image(device_image_interface &device, HWND owner, REFCLSID class_id, // FIXME: strip off archive names - opening a file inside an archive decompresses it to a temporary location std::wstring wfull = osd::text::to_wstring(full); Microsoft::WRL::ComPtr<IShellItem> item; - if (SUCCEEDED(SHCreateItemFromParsingName(wfull.c_str(), nullptr, IID_PPV_ARGS(item.GetAddressOf())))) + if (SUCCEEDED(SHCreateItemFromParsingName(wfull.c_str(), nullptr, IID_PPV_ARGS(&item)))) { //dialog->SetFolder(item); disabled until } @@ -171,7 +176,7 @@ void choose_image(device_image_interface &device, HWND owner, REFCLSID class_id, if (SUCCEEDED(hr)) { Microsoft::WRL::ComPtr<IShellItem> result; - hr = dialog->GetResult(result.GetAddressOf()); + hr = dialog->GetResult(&result); if (SUCCEEDED(hr)) { PWSTR selection = nullptr; @@ -209,7 +214,7 @@ consolewin_info::consolewin_info(debugger_windows_interface &debugger) : m_current_cpu(nullptr), m_devices_menu(nullptr) { - if ((window() == nullptr) || (m_views[0] == nullptr)) + if (!window() || !m_views[0]) goto cleanup; // create the views @@ -221,21 +226,30 @@ consolewin_info::consolewin_info(debugger_windows_interface &debugger) : goto cleanup; { - // Add image menu only if image devices exist + // add image menu only if image devices exist image_interface_enumerator iter(machine().root_device()); if (iter.first() != nullptr) { m_devices_menu = CreatePopupMenu(); for (device_image_interface &img : iter) { - if (!img.user_loadable()) - continue; - osd::text::tstring tc_buf = osd::text::to_tstring(string_format("%s : %s", img.device().name(), img.exists() ? img.filename() : "[no image]")); - AppendMenu(m_devices_menu, MF_ENABLED, 0, tc_buf.c_str()); + if (img.user_loadable()) + { + osd::text::tstring tc_buf = osd::text::to_tstring(string_format("%s : %s", img.device().name(), img.exists() ? img.filename() : "[no image]")); + AppendMenu(m_devices_menu, MF_ENABLED, 0, tc_buf.c_str()); + } } AppendMenu(GetMenu(window()), MF_ENABLED | MF_POPUP, (UINT_PTR)m_devices_menu, TEXT("Media")); } + // add the settings menu + HMENU const settingsmenu = CreatePopupMenu(); + AppendMenu(settingsmenu, MF_ENABLED, ID_SAVE_WINDOWS, TEXT("Save Window Arrangement")); + 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")); + AppendMenu(GetMenu(window()), MF_ENABLED | MF_POPUP, (UINT_PTR)settingsmenu, TEXT("Settings")); + // get the work bounds RECT work_bounds, bounds; SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0); @@ -346,7 +360,7 @@ void consolewin_info::update_menu() { disasmbasewin_info::update_menu(); - if (m_devices_menu != nullptr) + if (m_devices_menu) { // create the image menu uint32_t cnt = 0; @@ -432,70 +446,97 @@ void consolewin_info::update_menu() cnt++; } } + + HMENU const menu = GetMenu(window()); + CheckMenuItem(menu, ID_SAVE_WINDOWS, MF_BYCOMMAND | (debugger().get_save_window_arrangement() ? 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)); } bool consolewin_info::handle_command(WPARAM wparam, LPARAM lparam) { - if ((HIWORD(wparam) == 0) && (LOWORD(wparam) >= ID_DEVICE_OPTIONS)) + if (HIWORD(wparam) == 0) { - uint32_t const devid = (LOWORD(wparam) - ID_DEVICE_OPTIONS) / DEVOPTION_MAX; - image_interface_enumerator iter(machine().root_device()); - device_image_interface *const img = iter.byindex(devid); - if (img != nullptr) + if (LOWORD(wparam) >= ID_DEVICE_OPTIONS) { - switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX) - { - case DEVOPTION_ITEM: - // TODO: this is supposed to show a software list item picker - it never worked properly - return true; - case DEVOPTION_OPEN : - open_image_file(*img); - return true; - case DEVOPTION_CREATE: - create_image_file(*img); - return true; - case DEVOPTION_CLOSE: - img->unload(); - return true; - } - if (img->device().type() == CASSETTE) + uint32_t const devid = (LOWORD(wparam) - ID_DEVICE_OPTIONS) / DEVOPTION_MAX; + image_interface_enumerator iter(machine().root_device()); + device_image_interface *const img = iter.byindex(devid); + if (img != nullptr) { - auto *const cassette = downcast<cassette_image_device *>(&img->device()); - bool s; switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX) { - case DEVOPTION_CASSETTE_STOPPAUSE: - cassette->change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); + case DEVOPTION_ITEM: + // TODO: this is supposed to show a software list item picker - it never worked properly return true; - case DEVOPTION_CASSETTE_PLAY: - cassette->change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE); + case DEVOPTION_OPEN : + open_image_file(*img); return true; - case DEVOPTION_CASSETTE_RECORD: - cassette->change_state(CASSETTE_RECORD, CASSETTE_MASK_UISTATE); + case DEVOPTION_CREATE: + create_image_file(*img); return true; - case DEVOPTION_CASSETTE_REWIND: - cassette->seek(0.0, SEEK_SET); // to start + case DEVOPTION_CLOSE: + img->unload(); return true; - case DEVOPTION_CASSETTE_FASTFORWARD: - cassette->seek(+300.0, SEEK_CUR); // 5 minutes forward or end, whichever comes first - break; - case DEVOPTION_CASSETTE_MOTOR: - s =((cassette->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_DISABLED); - cassette->change_state(s ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); - break; - case DEVOPTION_CASSETTE_SOUND: - s =((cassette->get_state() & CASSETTE_MASK_SPEAKER) == CASSETTE_SPEAKER_MUTED); - cassette->change_state(s ? CASSETTE_SPEAKER_ENABLED : CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER); - break; + } + if (img->device().type() == CASSETTE) + { + auto *const cassette = downcast<cassette_image_device *>(&img->device()); + bool s; + switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX) + { + case DEVOPTION_CASSETTE_STOPPAUSE: + cassette->change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); + return true; + case DEVOPTION_CASSETTE_PLAY: + cassette->change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE); + return true; + case DEVOPTION_CASSETTE_RECORD: + cassette->change_state(CASSETTE_RECORD, CASSETTE_MASK_UISTATE); + return true; + case DEVOPTION_CASSETTE_REWIND: + cassette->seek(0.0, SEEK_SET); // to start + return true; + case DEVOPTION_CASSETTE_FASTFORWARD: + cassette->seek(+300.0, SEEK_CUR); // 5 minutes forward or end, whichever comes first + return true; + case DEVOPTION_CASSETTE_MOTOR: + s = ((cassette->get_state() & CASSETTE_MASK_MOTOR) == CASSETTE_MOTOR_DISABLED); + cassette->change_state(s ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); + return true; + case DEVOPTION_CASSETTE_SOUND: + s = ((cassette->get_state() & CASSETTE_MASK_SPEAKER) == CASSETTE_SPEAKER_MUTED); + cassette->change_state(s ? CASSETTE_SPEAKER_ENABLED : CASSETTE_SPEAKER_MUTED, CASSETTE_MASK_SPEAKER); + return true; + } } } } + else switch (LOWORD(wparam)) + { + case ID_SAVE_WINDOWS: + debugger().set_save_window_arrangement(!debugger().get_save_window_arrangement()); + return true; + case ID_LIGHT_BACKGROUND: + debugger().set_color_theme(ui_metrics::THEME_LIGHT_BACKGROUND); + return true; + case ID_DARK_BACKGROUND: + debugger().set_color_theme(ui_metrics::THEME_DARK_BACKGROUND); + return true; + } } return disasmbasewin_info::handle_command(wparam, lparam); } +void consolewin_info::save_configuration_to_node(util::xml::data_node &node) +{ + disasmbasewin_info::save_configuration_to_node(node); + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_CONSOLE); +} + + void consolewin_info::process_string(std::string const &string) { if (string.empty()) // an empty string is a single step @@ -515,9 +556,11 @@ void consolewin_info::open_image_file(device_image_interface &device) window(), CLSID_FileOpenDialog, true, - [&device] (std::string_view selection) + [this, &device] (std::string_view selection) { - device.load(selection); + auto [err, message] = device.load(selection); + if (err) + machine().debugger().console().printf("Error mounting image file: %s\n", !message.empty() ? message : err.message()); }); } @@ -529,9 +572,11 @@ void consolewin_info::create_image_file(device_image_interface &device) window(), CLSID_FileSaveDialog, false, - [&device] (std::string_view selection) + [this, &device] (std::string_view selection) { - device.create(selection, device.device_get_indexed_creatable_format(0), nullptr); + auto [err, message] = device.create(selection, device.device_get_indexed_creatable_format(0), nullptr); + if (err) + machine().debugger().console().printf("Error creating image file: %s\n", !message.empty() ? message : err.message()); }); } @@ -589,3 +634,5 @@ bool consolewin_info::get_softlist_info(device_image_interface &device) return passes_tests; } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/consolewininfo.h b/src/osd/modules/debugger/win/consolewininfo.h index 2d93895b64d..e79ba667a11 100644 --- a/src/osd/modules/debugger/win/consolewininfo.h +++ b/src/osd/modules/debugger/win/consolewininfo.h @@ -15,6 +15,8 @@ #include "disasmbasewininfo.h" +namespace osd::debugger::win { + class consolewin_info : public disasmbasewin_info { public: @@ -27,6 +29,7 @@ protected: virtual void recompute_children() override; virtual void update_menu() override; virtual bool handle_command(WPARAM wparam, LPARAM lparam) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; private: enum @@ -52,8 +55,10 @@ private: bool get_softlist_info(device_image_interface &img); device_t *m_current_cpu; - HMENU m_devices_menu; + HMENU m_devices_menu; std::map<std::string,std::string> slmap; }; +} // namespace osd::debugger::win + #endif // MAME_DEBUGGER_WIN_CONSOLEWININFO_H diff --git a/src/osd/modules/debugger/win/debugbaseinfo.cpp b/src/osd/modules/debugger/win/debugbaseinfo.cpp index 1be4f892da7..0de7331cbc8 100644 --- a/src/osd/modules/debugger/win/debugbaseinfo.cpp +++ b/src/osd/modules/debugger/win/debugbaseinfo.cpp @@ -10,6 +10,8 @@ #include "debugbaseinfo.h" +namespace osd::debugger::win { + debugbase_info::debugbase_info(debugger_windows_interface &debugger) : m_debugger(debugger), m_machine(debugger.machine()), @@ -60,3 +62,5 @@ void debugbase_info::smart_show_window(HWND wnd, bool show) if (visible != show) ShowWindow(wnd, show ? SW_SHOW : SW_HIDE); } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/debugbaseinfo.h b/src/osd/modules/debugger/win/debugbaseinfo.h index 15f4b845e7c..da874a6b8fd 100644 --- a/src/osd/modules/debugger/win/debugbaseinfo.h +++ b/src/osd/modules/debugger/win/debugbaseinfo.h @@ -13,6 +13,8 @@ #include "debugwin.h" +namespace osd::debugger::win { + class debugbase_info { protected: @@ -35,4 +37,6 @@ private: bool const &m_waiting_for_debugger; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_DEBUGBASEINFO_H diff --git a/src/osd/modules/debugger/win/debugviewinfo.cpp b/src/osd/modules/debugger/win/debugviewinfo.cpp index 6965d032dfa..8f3f05c16d2 100644 --- a/src/osd/modules/debugger/win/debugviewinfo.cpp +++ b/src/osd/modules/debugger/win/debugviewinfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// debugview.c - Win32 debug window handling +// debugviewinfo.cpp - Win32 debug window handling // //============================================================ @@ -12,9 +12,12 @@ #include "debugwininfo.h" #include "uimetrics.h" #include "debugger.h" + #include "debug/debugcon.h" #include "debug/debugcpu.h" +#include "util/xmlfile.h" + #include "strconv.h" #include "winutil.h" @@ -22,6 +25,8 @@ #include <mmsystem.h> +namespace osd::debugger::win { + // debugger view styles #define DEBUG_VIEW_STYLE WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN #define DEBUG_VIEW_STYLE_EX 0 @@ -180,6 +185,18 @@ void debugview_info::send_pagedown() } +int debugview_info::source_index() const +{ + if (m_view) + { + debug_view_source const *const source = m_view->source(); + if (source) + return m_view->source_index(*source); + } + return -1; +} + + char const *debugview_info::source_name() const { if (m_view) @@ -284,6 +301,56 @@ HWND debugview_info::create_source_combobox(HWND parent, LONG_PTR userdata) } +void debugview_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + if (m_view->cursor_supported()) + { + util::xml::data_node const *const selection = node.get_child(NODE_WINDOW_SELECTION); + if (selection) + { + debug_view_xy pos = m_view->cursor_position(); + m_view->set_cursor_visible(0 != selection->get_attribute_int(ATTR_SELECTION_CURSOR_VISIBLE, m_view->cursor_visible() ? 1 : 0)); + selection->get_attribute_int(ATTR_SELECTION_CURSOR_X, pos.x); + selection->get_attribute_int(ATTR_SELECTION_CURSOR_Y, pos.y); + m_view->set_cursor_position(pos); + } + } + + util::xml::data_node const *const scroll = node.get_child(NODE_WINDOW_SCROLL); + if (scroll) + { + debug_view_xy origin = m_view->visible_position(); + origin.x = scroll->get_attribute_int(ATTR_SCROLL_ORIGIN_X, origin.x * metrics().debug_font_width()) / metrics().debug_font_width(); + origin.y = scroll->get_attribute_int(ATTR_SCROLL_ORIGIN_Y, origin.y * metrics().debug_font_height()) / metrics().debug_font_height(); + m_view->set_visible_position(origin); + } +} + + +void debugview_info::save_configuration_to_node(util::xml::data_node &node) +{ + if (m_view->cursor_supported()) + { + util::xml::data_node *const selection = node.add_child(NODE_WINDOW_SELECTION, nullptr); + if (selection) + { + debug_view_xy const pos = m_view->cursor_position(); + selection->set_attribute_int(ATTR_SELECTION_CURSOR_VISIBLE, m_view->cursor_visible() ? 1 : 0); + selection->set_attribute_int(ATTR_SELECTION_CURSOR_X, pos.x); + selection->set_attribute_int(ATTR_SELECTION_CURSOR_Y, pos.y); + } + } + + util::xml::data_node *const scroll = node.add_child(NODE_WINDOW_SCROLL, nullptr); + if (scroll) + { + debug_view_xy const origin = m_view->visible_position(); + scroll->set_attribute_int(ATTR_SCROLL_ORIGIN_X, origin.x * metrics().debug_font_width()); + scroll->set_attribute_int(ATTR_SCROLL_ORIGIN_Y, origin.y * metrics().debug_font_height()); + } +} + + void debugview_info::add_items_to_context_menu(HMENU menu) { AppendMenu(menu, MF_ENABLED, ID_CONTEXT_COPY_VISIBLE, TEXT("Copy Visible")); @@ -433,7 +500,7 @@ void debugview_info::draw_contents(HDC windc) for (int iter = 0; iter < 2; iter++) { COLORREF fgcolor; - COLORREF bgcolor = RGB(0xff,0xff,0xff); + COLORREF bgcolor = metrics().view_colors(DCA_NORMAL).second; HBRUSH bgbrush = nullptr; int last_attrib = -1; TCHAR buffer[256]; @@ -457,20 +524,8 @@ void debugview_info::draw_contents(HDC windc) { COLORREF oldbg = bgcolor; - // pick new background color - bgcolor = RGB(0xff,0xff,0xff); - if (viewdata[col].attrib & DCA_VISITED) bgcolor = RGB(0xc6, 0xe2, 0xff); - if (viewdata[col].attrib & DCA_ANCILLARY) bgcolor = RGB(0xe0,0xe0,0xe0); - if (viewdata[col].attrib & DCA_SELECTED) bgcolor = RGB(0xff,0x80,0x80); - if (viewdata[col].attrib & DCA_CURRENT) bgcolor = RGB(0xff,0xff,0x00); - if ((viewdata[col].attrib & DCA_SELECTED) && (viewdata[col].attrib & DCA_CURRENT)) bgcolor = RGB(0xff,0xc0,0x80); - - // pick new foreground color - fgcolor = RGB(0x00,0x00,0x00); - if (viewdata[col].attrib & DCA_CHANGED) fgcolor = RGB(0xff,0x00,0x00); - if (viewdata[col].attrib & DCA_INVALID) fgcolor = RGB(0x00,0x00,0xff); - if (viewdata[col].attrib & DCA_DISABLED) fgcolor = RGB((GetRValue(fgcolor) + GetRValue(bgcolor)) / 2, (GetGValue(fgcolor) + GetGValue(bgcolor)) / 2, (GetBValue(fgcolor) + GetBValue(bgcolor)) / 2); - if (viewdata[col].attrib & DCA_COMMENT) fgcolor = RGB(0x00,0x80,0x00); + // pick new colors + std::tie(fgcolor, bgcolor) = metrics().view_colors(viewdata[col].attrib); // flush any pending drawing if (count > 0) @@ -481,7 +536,7 @@ void debugview_info::draw_contents(HDC windc) FillRect(dc, &bounds, bgbrush); if (do_filldown) { - COLORREF const filldown = (last_attrib & DCA_ANCILLARY) ? RGB(0xe0,0xe0,0xe0) : RGB(0xff,0xff,0xff); + COLORREF const filldown = metrics().view_colors(last_attrib & DCA_ANCILLARY).second; if (oldbg != filldown) { DeleteObject(bgbrush); @@ -528,7 +583,7 @@ void debugview_info::draw_contents(HDC windc) FillRect(dc, &bounds, bgbrush); if (do_filldown) { - COLORREF const filldown = (last_attrib & DCA_ANCILLARY) ? RGB(0xe0,0xe0,0xe0) : RGB(0xff,0xff,0xff); + COLORREF const filldown = metrics().view_colors(last_attrib & DCA_ANCILLARY).second; if (bgcolor != filldown) { DeleteObject(bgbrush); @@ -1021,3 +1076,5 @@ void debugview_info::register_window_class() s_window_class_registered = true; } } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/debugviewinfo.h b/src/osd/modules/debugger/win/debugviewinfo.h index beb5ee66ff0..a009c368441 100644 --- a/src/osd/modules/debugger/win/debugviewinfo.h +++ b/src/osd/modules/debugger/win/debugviewinfo.h @@ -17,6 +17,8 @@ #include "debug/debugvw.h" +namespace osd::debugger::win { + class debugview_info : protected debugbase_info { public: @@ -42,6 +44,7 @@ public: bool cursor_supported() const { return m_view->cursor_supported(); } bool cursor_visible() const { return m_view->cursor_visible(); } + int source_index() const; char const *source_name() const; device_t *source_device() const; bool source_is_visible_cpu() const; @@ -51,6 +54,9 @@ public: HWND create_source_combobox(HWND parent, LONG_PTR userdata); + virtual void restore_configuration_from_node(util::xml::data_node const &node); + virtual void save_configuration_to_node(util::xml::data_node &node); + protected: enum { @@ -86,4 +92,6 @@ private: static bool s_window_class_registered; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_DEBUGVIEWINFO_H diff --git a/src/osd/modules/debugger/win/debugwin.h b/src/osd/modules/debugger/win/debugwin.h index e999b87334f..52303eefc2c 100644 --- a/src/osd/modules/debugger/win/debugwin.h +++ b/src/osd/modules/debugger/win/debugwin.h @@ -10,6 +10,8 @@ #pragma once +#include "../xmlconfig.h" + // standard windows headers #include <windows.h> #include <windowsx.h> @@ -20,6 +22,8 @@ #endif +namespace osd::debugger::win { + class debugview_info; class debugwin_info; class ui_metrics; @@ -33,6 +37,9 @@ public: virtual running_machine &machine() const = 0; virtual ui_metrics &metrics() const = 0; + 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 const &waiting_for_debugger() const = 0; virtual bool seq_pressed() const = 0; @@ -45,6 +52,10 @@ public: virtual void show_all() = 0; virtual void hide_all() = 0; + + virtual void stagger_window(HWND window, int width, int height) = 0; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_DEBUGWIN_H diff --git a/src/osd/modules/debugger/win/debugwininfo.cpp b/src/osd/modules/debugger/win/debugwininfo.cpp index fdd486f3e80..8c3275a7549 100644 --- a/src/osd/modules/debugger/win/debugwininfo.cpp +++ b/src/osd/modules/debugger/win/debugwininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// debugwininfo.c - Win32 debug window handling +// debugwininfo.cpp - Win32 debug window handling // //============================================================ @@ -14,12 +14,19 @@ #include "debugger.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" + +#include "util/xmlfile.h" + #include "window.h" #include "winutf8.h" - #include "winutil.h" + #include "modules/lib/osdobj_common.h" +#include <cstring> + + +namespace osd::debugger::win { bool debugwin_info::s_window_class_registered = false; @@ -37,9 +44,14 @@ debugwin_info::debugwin_info(debugger_windows_interface &debugger, bool is_main_ { register_window_class(); - m_wnd = win_create_window_ex_utf8(DEBUG_WINDOW_STYLE_EX, "MAMEDebugWindow", title, DEBUG_WINDOW_STYLE, - 0, 0, 100, 100, std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(), create_standard_menubar(), GetModuleHandleUni(), this); - if (m_wnd == nullptr) + 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(), + create_standard_menubar(), + GetModuleHandleUni(), + this); + if (!m_wnd) return; RECT work_bounds; @@ -54,6 +66,12 @@ debugwin_info::~debugwin_info() } +void debugwin_info::redraw() +{ + RedrawWindow(m_wnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN); +} + + void debugwin_info::destroy() { for (int curview = 0; curview < MAX_VIEWS; curview++) @@ -251,6 +269,89 @@ bool debugwin_info::handle_key(WPARAM wparam, LPARAM lparam) } +void debugwin_info::save_configuration(util::xml::data_node &parentnode) +{ + util::xml::data_node *const node = parentnode.add_child(NODE_WINDOW, nullptr); + if (node) + save_configuration_to_node(*node); +} + + +void debugwin_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + // get current size to use for defaults + RECT bounds; + POINT origin; + origin.x = 0; + origin.y = 0; + if (!GetClientRect(window(), &bounds) && ClientToScreen(window(), &origin)) + return; + + // get saved size and adjust for window chrome + RECT desired; + desired.left = node.get_attribute_int(ATTR_WINDOW_POSITION_X, origin.x); + desired.top = node.get_attribute_int(ATTR_WINDOW_POSITION_Y, origin.y); + desired.right = desired.left + node.get_attribute_int(ATTR_WINDOW_WIDTH, bounds.right); + desired.bottom = desired.top + node.get_attribute_int(ATTR_WINDOW_HEIGHT, bounds.bottom); + // TODO: sanity checks... + if (!AdjustWindowRectEx(&desired, DEBUG_WINDOW_STYLE, GetMenu(window()) ? TRUE : FALSE, DEBUG_WINDOW_STYLE_EX)) + return; + + // actually move the window + MoveWindow( + window(), + desired.left, + desired.top, + desired.right - desired.left, + desired.bottom - desired.top, + TRUE); + + // restrict to one monitor and avoid toolbars + HMONITOR const nearest_monitor = MonitorFromWindow(window(), MONITOR_DEFAULTTONEAREST); + if (nearest_monitor) + { + MONITORINFO info; + std::memset(&info, 0, sizeof(info)); + info.cbSize = sizeof(info); + if (GetMonitorInfo(nearest_monitor, &info)) + { + if (desired.right > info.rcWork.right) + { + desired.left -= desired.right - info.rcWork.right; + desired.right = info.rcWork.right; + } + if (desired.bottom > info.rcWork.bottom) + { + desired.top -= desired.bottom - info.rcWork.bottom; + desired.bottom = info.rcWork.bottom; + } + if (desired.left < info.rcWork.left) + { + desired.right += info.rcWork.left - desired.left; + desired.left = info.rcWork.left; + } + if (desired.top < info.rcWork.top) + { + desired.bottom += info.rcWork.top - desired.top; + desired.top = info.rcWork.top; + } + desired.bottom = std::min(info.rcWork.bottom, desired.bottom); + desired.right = std::min(info.rcWork.right, desired.right); + MoveWindow( + window(), + desired.left, + desired.top, + desired.right - desired.left, + desired.bottom - desired.top, + TRUE); + } + } + + // sort out contents + recompute_children(); +} + + void debugwin_info::recompute_children() { if (m_views[0] != nullptr) @@ -392,6 +493,22 @@ void debugwin_info::draw_border(HDC dc, RECT &bounds) } +void debugwin_info::save_configuration_to_node(util::xml::data_node &node) +{ + RECT bounds; + POINT origin; + origin.x = 0; + origin.y = 0; + if (GetClientRect(window(), &bounds) && ClientToScreen(window(), &origin)) + { + node.set_attribute_int(ATTR_WINDOW_POSITION_X, origin.x); + node.set_attribute_int(ATTR_WINDOW_POSITION_Y, origin.y); + node.set_attribute_int(ATTR_WINDOW_WIDTH, bounds.right); + node.set_attribute_int(ATTR_WINDOW_HEIGHT, bounds.bottom); + } +} + + void debugwin_info::draw_border(HDC dc, HWND child) { RECT bounds; @@ -620,3 +737,5 @@ void debugwin_info::register_window_class() s_window_class_registered = true; } } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/debugwininfo.h b/src/osd/modules/debugger/win/debugwininfo.h index 20cea0d477d..f6f37c86955 100644 --- a/src/osd/modules/debugger/win/debugwininfo.h +++ b/src/osd/modules/debugger/win/debugwininfo.h @@ -15,10 +15,11 @@ #include "debugbaseinfo.h" +namespace osd::debugger::win { + class debugwin_info : protected debugbase_info { public: - debugwin_info(debugger_windows_interface &debugger, bool is_main_console, LPCSTR title, WNDPROC handler); virtual ~debugwin_info(); bool is_valid() const { return m_wnd != nullptr; } @@ -40,6 +41,7 @@ public: void show() const { smart_show_window(m_wnd, true); } void hide() const { smart_show_window(m_wnd, false); } void set_foreground() const { SetForegroundWindow(m_wnd); } + void redraw(); void destroy(); virtual bool set_default_focus(); @@ -49,6 +51,9 @@ public: virtual bool handle_key(WPARAM wparam, LPARAM lparam); + void save_configuration(util::xml::data_node &parentnode); + virtual void restore_configuration_from_node(util::xml::data_node const &node); + protected: static DWORD const DEBUG_WINDOW_STYLE = (WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN) & (~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX); static DWORD const DEBUG_WINDOW_STYLE_EX = 0; @@ -108,9 +113,15 @@ protected: ID_CLEAR_LOG, + ID_SAVE_WINDOWS, + ID_LIGHT_BACKGROUND, + ID_DARK_BACKGROUND, + ID_DEVICE_OPTIONS // always keep this at the end }; + debugwin_info(debugger_windows_interface &debugger, bool is_main_console, LPCSTR title, WNDPROC handler); + bool is_main_console() const { return m_is_main_console; } HWND window() const { return m_wnd; } uint32_t minwidth() const { return m_minwidth; } @@ -125,6 +136,8 @@ protected: void draw_border(HDC dc, RECT &bounds); void draw_border(HDC dc, HWND child); + virtual void save_configuration_to_node(util::xml::data_node &node); + std::unique_ptr<debugview_info> m_views[MAX_VIEWS]; private: @@ -141,12 +154,14 @@ private: HWND m_wnd; WNDPROC const m_handler; - uint32_t m_minwidth, m_maxwidth; - uint32_t m_minheight, m_maxheight; + uint32_t m_minwidth, m_maxwidth; + uint32_t m_minheight, m_maxheight; - uint16_t m_ignore_char_lparam; + uint16_t m_ignore_char_lparam; static bool s_window_class_registered; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_DEBUGWININFO_H diff --git a/src/osd/modules/debugger/win/disasmbasewininfo.cpp b/src/osd/modules/debugger/win/disasmbasewininfo.cpp index f68a7652c77..48771c4f133 100644 --- a/src/osd/modules/debugger/win/disasmbasewininfo.cpp +++ b/src/osd/modules/debugger/win/disasmbasewininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// disasmbasewininfo.c - Win32 debug window handling +// disasmbasewininfo.cpp - Win32 debug window handling // //============================================================ @@ -20,6 +20,8 @@ //#include "winutf8.h" +namespace osd::debugger::win { + disasmbasewin_info::disasmbasewin_info(debugger_windows_interface &debugger, bool is_main_console, LPCSTR title, WNDPROC handler) : editwin_info(debugger, is_main_console, title, handler) { @@ -27,7 +29,7 @@ disasmbasewin_info::disasmbasewin_info(debugger_windows_interface &debugger, boo return; m_views[0].reset(new disasmview_info(debugger, *this, window())); - if ((m_views[0] == nullptr) || !m_views[0]->is_valid()) + if (!m_views[0] || !m_views[0]->is_valid()) { m_views[0].reset(); return; @@ -118,7 +120,7 @@ void disasmbasewin_info::update_menu() // first find an existing breakpoint at this address const debug_breakpoint *bp = debug->breakpoint_find(address); - if (bp == nullptr) + if (!bp) { ModifyMenu(menu, ID_TOGGLE_BREAKPOINT, MF_BYCOMMAND, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9")); ModifyMenu(menu, ID_DISABLE_BREAKPOINT, MF_BYCOMMAND, ID_DISABLE_BREAKPOINT, TEXT("Disable breakpoint at cursor\tShift+F9")); @@ -131,7 +133,7 @@ void disasmbasewin_info::update_menu() else ModifyMenu(menu, ID_DISABLE_BREAKPOINT, MF_BYCOMMAND, ID_DISABLE_BREAKPOINT, TEXT("Enable breakpoint at cursor\tShift+F9")); } - bool const available = (bp != nullptr) && (!is_main_console() || dasmview->source_is_visible_cpu()); + bool const available = bp && (!is_main_console() || dasmview->source_is_visible_cpu()); EnableMenuItem(menu, ID_DISABLE_BREAKPOINT, MF_BYCOMMAND | (available ? MF_ENABLED : MF_GRAYED)); } else @@ -174,7 +176,7 @@ bool disasmbasewin_info::handle_command(WPARAM wparam, LPARAM lparam) { if (bp == nullptr) { - int32_t bpindex = debug->breakpoint_set(address, nullptr, nullptr); + int32_t bpindex = debug->breakpoint_set(address); machine().debugger().console().printf("Breakpoint %X set\n", bpindex); } else @@ -263,3 +265,19 @@ bool disasmbasewin_info::handle_command(WPARAM wparam, LPARAM lparam) } return editwin_info::handle_command(wparam, lparam); } + + +void disasmbasewin_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + editwin_info::restore_configuration_from_node(node); + m_views[0]->restore_configuration_from_node(node); +} + + +void disasmbasewin_info::save_configuration_to_node(util::xml::data_node &node) +{ + editwin_info::save_configuration_to_node(node); + m_views[0]->save_configuration_to_node(node); +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/disasmbasewininfo.h b/src/osd/modules/debugger/win/disasmbasewininfo.h index c22f7f08e78..1ef8245b5a0 100644 --- a/src/osd/modules/debugger/win/disasmbasewininfo.h +++ b/src/osd/modules/debugger/win/disasmbasewininfo.h @@ -15,6 +15,8 @@ #include "editwininfo.h" +namespace osd::debugger::win { + class disasmbasewin_info : public editwin_info { public: @@ -22,10 +24,14 @@ public: virtual ~disasmbasewin_info(); virtual bool handle_key(WPARAM wparam, LPARAM lparam) override; + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; protected: virtual void update_menu() override; virtual bool handle_command(WPARAM wparam, LPARAM lparam) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; }; +} // namespace osd::debugger::win + #endif // MAME_DEBUGGER_WIN_DISASMBASEWININFO_H diff --git a/src/osd/modules/debugger/win/disasmviewinfo.cpp b/src/osd/modules/debugger/win/disasmviewinfo.cpp index 8884bee5195..3cf9dbd70ff 100644 --- a/src/osd/modules/debugger/win/disasmviewinfo.cpp +++ b/src/osd/modules/debugger/win/disasmviewinfo.cpp @@ -2,13 +2,17 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// disasmviewinfo.c - Win32 debug window handling +// disasmviewinfo.cpp - Win32 debug window handling // //============================================================ #include "emu.h" #include "disasmviewinfo.h" +#include "util/xmlfile.h" + + +namespace osd::debugger::win { disasmview_info::disasmview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent) : debugview_info(debugger, owner, parent, DVT_DISASSEMBLY) @@ -21,6 +25,12 @@ disasmview_info::~disasmview_info() } +char const *disasmview_info::expression() const +{ + return view<debug_view_disasm>()->expression(); +} + + disasm_right_column disasmview_info::right_column() const { return view<debug_view_disasm>()->right_column(); @@ -43,3 +53,23 @@ void disasmview_info::set_right_column(disasm_right_column contents) { view<debug_view_disasm>()->set_right_column(contents); } + + +void disasmview_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + debug_view_disasm &dasmview(*view<debug_view_disasm>()); + dasmview.set_right_column(disasm_right_column(node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()))); + + debugview_info::restore_configuration_from_node(node); +} + + +void disasmview_info::save_configuration_to_node(util::xml::data_node &node) +{ + debugview_info::save_configuration_to_node(node); + + debug_view_disasm &dasmview(*view<debug_view_disasm>()); + node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN, dasmview.right_column()); +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/disasmviewinfo.h b/src/osd/modules/debugger/win/disasmviewinfo.h index 0685d762346..809f92b0c14 100644 --- a/src/osd/modules/debugger/win/disasmviewinfo.h +++ b/src/osd/modules/debugger/win/disasmviewinfo.h @@ -17,17 +17,25 @@ #include "debug/dvdisasm.h" +namespace osd::debugger::win { + class disasmview_info : public debugview_info { public: disasmview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent); virtual ~disasmview_info(); + char const *expression() const; disasm_right_column right_column() const; offs_t selected_address() const; void set_expression(const std::string &expression); void set_right_column(disasm_right_column contents); + + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; }; +} // namespace osd::debugger::win + #endif // MAME_DEBUGGER_WIN_DISASMVIEWINFO_H diff --git a/src/osd/modules/debugger/win/disasmwininfo.cpp b/src/osd/modules/debugger/win/disasmwininfo.cpp index b94eadbba03..6b38609ace8 100644 --- a/src/osd/modules/debugger/win/disasmwininfo.cpp +++ b/src/osd/modules/debugger/win/disasmwininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// disasmwininfo.c - Win32 debug window handling +// disasmwininfo.cpp - Win32 debug window handling // //============================================================ @@ -12,9 +12,14 @@ #include "debugviewinfo.h" #include "disasmviewinfo.h" #include "uimetrics.h" + +#include "util/xmlfile.h" + #include "winutf8.h" +namespace osd::debugger::win { + disasmwin_info::disasmwin_info(debugger_windows_interface &debugger) : disasmbasewin_info(debugger, false, "Disassembly", nullptr), m_combownd(nullptr) @@ -34,11 +39,11 @@ disasmwin_info::disasmwin_info(debugger_windows_interface &debugger) : update_caption(); // recompute the children once to get the maxwidth - disasmwin_info::recompute_children(); + recompute_children(); // position the window and recompute children again - SetWindowPos(window(), HWND_TOP, 100, 100, maxwidth(), 200, SWP_SHOWWINDOW); - disasmwin_info::recompute_children(); + debugger.stagger_window(window(), maxwidth(), 200); + recompute_children(); // mark the edit box as the default focus and set it editwin_info::set_default_focus(); @@ -143,3 +148,34 @@ void disasmwin_info::update_caption() { win_set_window_text_utf8(window(), std::string("Disassembly: ").append(m_views[0]->source_name()).c_str()); } + + +void disasmwin_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + m_views[0]->set_source_index(node.get_attribute_int(ATTR_WINDOW_DISASSEMBLY_CPU, m_views[0]->source_index())); + int const cursource = m_views[0]->source_index(); + if (0 <= cursource) + SendMessage(m_combownd, CB_SETCURSEL, cursource, 0); + update_caption(); + + util::xml::data_node const *const expr = node.get_child(NODE_WINDOW_EXPRESSION); + if (expr && expr->get_value()) + { + set_editwnd_text(expr->get_value()); + process_string(expr->get_value()); + } + + disasmbasewin_info::restore_configuration_from_node(node); +} + + +void disasmwin_info::save_configuration_to_node(util::xml::data_node &node) +{ + disasmbasewin_info::save_configuration_to_node(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_DISASSEMBLY_VIEWER); + node.set_attribute_int(ATTR_WINDOW_DISASSEMBLY_CPU, m_views[0]->source_index()); + node.add_child(NODE_WINDOW_EXPRESSION, downcast<disasmview_info *>(m_views[0].get())->expression()); +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/disasmwininfo.h b/src/osd/modules/debugger/win/disasmwininfo.h index 71764bab0ef..e5a5ceaaa0e 100644 --- a/src/osd/modules/debugger/win/disasmwininfo.h +++ b/src/osd/modules/debugger/win/disasmwininfo.h @@ -15,16 +15,21 @@ #include "disasmbasewininfo.h" +namespace osd::debugger::win { + class disasmwin_info : public disasmbasewin_info { public: disasmwin_info(debugger_windows_interface &debugger); virtual ~disasmwin_info(); + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; + protected: virtual void recompute_children() override; virtual bool handle_command(WPARAM wparam, LPARAM lparam) override; virtual void draw_contents(HDC dc) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; private: virtual void process_string(const std::string &string) override; @@ -34,4 +39,6 @@ private: HWND m_combownd; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_DISASMWININFO_H diff --git a/src/osd/modules/debugger/win/editwininfo.cpp b/src/osd/modules/debugger/win/editwininfo.cpp index d7959afd5f1..c62d2b1f4f7 100644 --- a/src/osd/modules/debugger/win/editwininfo.cpp +++ b/src/osd/modules/debugger/win/editwininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// editwininfo.c - Win32 debug window handling +// editwininfo.cpp - Win32 debug window handling // //============================================================ @@ -12,18 +12,22 @@ #include "debugviewinfo.h" #include "uimetrics.h" +#include "xmlfile.h" + #include "strconv.h" #include "winutil.h" +namespace osd::debugger::win { + namespace { constexpr DWORD EDIT_BOX_STYLE = WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL; constexpr DWORD EDIT_BOX_STYLE_EX = 0; constexpr int MAX_EDIT_STRING = 256; -constexpr int HISTORY_LENGTH = 20; +constexpr int HISTORY_LENGTH = 100; } // anonymous namespace @@ -34,7 +38,7 @@ editwin_info::editwin_info(debugger_windows_interface &debugger, bool is_main_co m_edit_defstr(), m_original_editproc(nullptr), m_history(), - m_last_history(0) + m_last_history(-1) { if (window() == nullptr) return; @@ -105,6 +109,43 @@ void editwin_info::draw_contents(HDC dc) } +void editwin_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + m_history.clear(); + util::xml::data_node const *const hist = node.get_child(NODE_WINDOW_HISTORY); + if (hist) + { + util::xml::data_node const *item = hist->get_child(NODE_HISTORY_ITEM); + while (item) + { + if (item->get_value() && *item->get_value()) + { + while (m_history.size() >= HISTORY_LENGTH) + m_history.pop_back(); + m_history.emplace_front(osd::text::to_tstring(item->get_value())); + } + item = item->get_next_sibling(NODE_HISTORY_ITEM); + } + } + m_last_history = -1; + + debugwin_info::restore_configuration_from_node(node); +} + + +void editwin_info::save_configuration_to_node(util::xml::data_node &node) +{ + debugwin_info::save_configuration_to_node(node); + + util::xml::data_node *const hist = node.add_child(NODE_WINDOW_HISTORY, nullptr); + if (hist) + { + for (auto it = m_history.crbegin(); m_history.crend() != it; ++it) + hist->add_child(NODE_HISTORY_ITEM, osd::text::from_tstring(*it).c_str()); + } +} + + LRESULT editwin_info::edit_proc(UINT message, WPARAM wparam, LPARAM lparam) { // handle a few messages @@ -194,7 +235,7 @@ LRESULT editwin_info::edit_proc(UINT message, WPARAM wparam, LPARAM lparam) m_history.pop_back(); m_history.emplace_front(buffer); } - m_last_history = m_history.size() - 1; + m_last_history = -1; // process { @@ -241,3 +282,5 @@ LRESULT CALLBACK editwin_info::static_edit_proc(HWND wnd, UINT message, WPARAM w assert(info->m_editwnd == wnd); return info->edit_proc(message, wparam, lparam); } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/editwininfo.h b/src/osd/modules/debugger/win/editwininfo.h index 2156d8ba68d..e2c0ce3c4b8 100644 --- a/src/osd/modules/debugger/win/editwininfo.h +++ b/src/osd/modules/debugger/win/editwininfo.h @@ -18,6 +18,8 @@ #include <string> +namespace osd::debugger::win { + class editwin_info : public debugwin_info { public: @@ -28,6 +30,8 @@ public: virtual bool set_default_focus() override; + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; + protected: constexpr static DWORD COMBO_BOX_STYLE = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL; constexpr static DWORD COMBO_BOX_STYLE_EX = 0; @@ -39,6 +43,8 @@ protected: virtual void draw_contents(HDC dc) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; + private: typedef std::deque<std::basic_string<TCHAR> > history_deque; @@ -55,4 +61,6 @@ private: int m_last_history; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_EDITWININFO_H diff --git a/src/osd/modules/debugger/win/logviewinfo.cpp b/src/osd/modules/debugger/win/logviewinfo.cpp index affd4acd17a..c6bbdfacb88 100644 --- a/src/osd/modules/debugger/win/logviewinfo.cpp +++ b/src/osd/modules/debugger/win/logviewinfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Samuele Zannoli //============================================================ // -// logviewinfo.c - Win32 debug log window handling +// logviewinfo.cpp - Win32 debug log window handling // //============================================================ @@ -12,6 +12,8 @@ #include "debug/dvtext.h" +namespace osd::debugger::win { + logview_info::logview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent) : debugview_info(debugger, owner, parent, DVT_LOG) { @@ -27,3 +29,5 @@ void logview_info::clear() { view<debug_view_log>()->clear(); } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/logviewinfo.h b/src/osd/modules/debugger/win/logviewinfo.h index d7ca0df8c9e..15de2a3a095 100644 --- a/src/osd/modules/debugger/win/logviewinfo.h +++ b/src/osd/modules/debugger/win/logviewinfo.h @@ -15,6 +15,8 @@ #include "debugviewinfo.h" +namespace osd::debugger::win { + class logview_info : public debugview_info { public: @@ -24,4 +26,6 @@ public: void clear(); }; +} // namespace osd::debugger::win + #endif // MAME_DEBUGGER_WIN_LOGVIEWINFO_H diff --git a/src/osd/modules/debugger/win/logwininfo.cpp b/src/osd/modules/debugger/win/logwininfo.cpp index 88b6022aa9a..40515906bb1 100644 --- a/src/osd/modules/debugger/win/logwininfo.cpp +++ b/src/osd/modules/debugger/win/logwininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// logwininfo.c - Win32 debug window handling +// logwininfo.cpp - Win32 debug window handling // //============================================================ @@ -12,6 +12,10 @@ #include "debugviewinfo.h" #include "logviewinfo.h" +#include "util/xmlfile.h" + + +namespace osd::debugger::win { logwin_info::logwin_info(debugger_windows_interface &debugger) : debugwin_info(debugger, false, std::string("Errorlog: ").append(debugger.machine().system().type.fullname()).append(" [").append(debugger.machine().system().name).append("]").c_str(), nullptr) @@ -41,11 +45,9 @@ logwin_info::logwin_info(debugger_windows_interface &debugger) : // clamp the min/max size set_maxwidth(bounds.right - bounds.left); - // position the window at the bottom-right - SetWindowPos(window(), HWND_TOP, 100, 100, bounds.right - bounds.left, bounds.bottom - bounds.top, SWP_SHOWWINDOW); - - // recompute the children - debugwin_info::recompute_children(); + // position the window and recompute children + debugger.stagger_window(window(), bounds.right - bounds.left, bounds.bottom - bounds.top); + recompute_children(); } @@ -63,3 +65,12 @@ bool logwin_info::handle_command(WPARAM wparam, LPARAM lparam) } return debugwin_info::handle_command(wparam, lparam); } + + +void logwin_info::save_configuration_to_node(util::xml::data_node &node) +{ + debugwin_info::save_configuration_to_node(node); + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_ERROR_LOG_VIEWER); +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/logwininfo.h b/src/osd/modules/debugger/win/logwininfo.h index b40d184485e..4e822f1b534 100644 --- a/src/osd/modules/debugger/win/logwininfo.h +++ b/src/osd/modules/debugger/win/logwininfo.h @@ -15,6 +15,8 @@ #include "debugwininfo.h" +namespace osd::debugger::win { + class logwin_info : public debugwin_info { public: @@ -23,6 +25,9 @@ public: protected: virtual bool handle_command(WPARAM wparam, LPARAM lparam) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_LOGWININFO_H diff --git a/src/osd/modules/debugger/win/memoryviewinfo.cpp b/src/osd/modules/debugger/win/memoryviewinfo.cpp index a8891553987..90e23a557a9 100644 --- a/src/osd/modules/debugger/win/memoryviewinfo.cpp +++ b/src/osd/modules/debugger/win/memoryviewinfo.cpp @@ -2,16 +2,20 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// memoryviewinfo.c - Win32 debug window handling +// memoryviewinfo.cpp - Win32 debug window handling // //============================================================ #include "emu.h" #include "memoryviewinfo.h" +#include "util/xmlfile.h" + #include "strconv.h" +namespace osd::debugger::win { + memoryview_info::memoryview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent) : debugview_info(debugger, owner, parent, DVT_MEMORY) { @@ -23,6 +27,12 @@ memoryview_info::~memoryview_info() } +char const *memoryview_info::expression() const +{ + return view<debug_view_memory>()->expression(); +} + + debug_view_memory::data_format memoryview_info::data_format() const { return view<debug_view_memory>()->get_data_format(); @@ -83,6 +93,32 @@ void memoryview_info::set_address_radix(int radix) } +void memoryview_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + debug_view_memory &memview(*view<debug_view_memory>()); + memview.set_reverse(0 != node.get_attribute_int(ATTR_WINDOW_MEMORY_REVERSE_COLUMNS, memview.reverse() ? 1 : 0)); + memview.set_physical(0 != node.get_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_MODE, memview.physical() ? 1 : 0)); + memview.set_address_radix(node.get_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_RADIX, memview.address_radix())); + memview.set_data_format(debug_view_memory::data_format(node.get_attribute_int(ATTR_WINDOW_MEMORY_DATA_FORMAT, int(memview.get_data_format())))); + memview.set_chunks_per_row(node.get_attribute_int(ATTR_WINDOW_MEMORY_ROW_CHUNKS, memview.chunks_per_row())); + + debugview_info::restore_configuration_from_node(node); +} + + +void memoryview_info::save_configuration_to_node(util::xml::data_node &node) +{ + debugview_info::save_configuration_to_node(node); + + debug_view_memory &memview(*view<debug_view_memory>()); + node.set_attribute_int(ATTR_WINDOW_MEMORY_REVERSE_COLUMNS, memview.reverse() ? 1 : 0); + node.set_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_MODE, memview.physical() ? 1 : 0); + node.set_attribute_int(ATTR_WINDOW_MEMORY_ADDRESS_RADIX, memview.address_radix()); + node.set_attribute_int(ATTR_WINDOW_MEMORY_DATA_FORMAT, int(memview.get_data_format())); + node.set_attribute_int(ATTR_WINDOW_MEMORY_ROW_CHUNKS, memview.chunks_per_row()); +} + + void memoryview_info::add_items_to_context_menu(HMENU menu) { debugview_info::add_items_to_context_menu(menu); @@ -107,29 +143,30 @@ void memoryview_info::update_context_menu(HMENU menu) debug_view_xy const pos = memview.cursor_position(); offs_t const address = space->byte_to_address(memview.addressAtCursorPosition(pos)); offs_t a = address & space->logaddrmask(); - if (!space->device().memory().translate(space->spacenum(), TRANSLATE_READ_DEBUG, a)) + address_space *tspace; + if (!space->device().memory().translate(space->spacenum(), device_memory_interface::TR_READ, a, tspace)) { m_lastpc = "Bad address"; } else { - uint64_t memval = space->unmap(); - auto dis = space->device().machine().disable_side_effects(); - switch (space->data_width()) + uint64_t memval = tspace->unmap(); + auto dis = tspace->device().machine().disable_side_effects(); + switch (tspace->data_width()) { - case 8: memval = space->read_byte(a); break; - case 16: memval = space->read_word_unaligned(a); break; - case 32: memval = space->read_dword_unaligned(a); break; - case 64: memval = space->read_qword_unaligned(a); break; + case 8: memval = tspace->read_byte(a); break; + case 16: memval = tspace->read_word_unaligned(a); break; + case 32: memval = tspace->read_dword_unaligned(a); break; + case 64: memval = tspace->read_qword_unaligned(a); break; } offs_t const pc = source.device()->debug()->track_mem_pc_from_space_address_data( - space->spacenum(), + tspace->spacenum(), address, memval); if (pc != offs_t(-1)) { - if (space->is_octal()) + if (tspace->is_octal()) m_lastpc = util::string_format("Address %o written at PC=%o", address, pc); else m_lastpc = util::string_format("Address %x written at PC=%x", address, pc); @@ -167,3 +204,5 @@ void memoryview_info::handle_context_menu(unsigned command) }; } + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/memoryviewinfo.h b/src/osd/modules/debugger/win/memoryviewinfo.h index ccc451acd3e..514c6c13ec6 100644 --- a/src/osd/modules/debugger/win/memoryviewinfo.h +++ b/src/osd/modules/debugger/win/memoryviewinfo.h @@ -18,12 +18,15 @@ #include <string> +namespace osd::debugger::win { + class memoryview_info : public debugview_info { public: memoryview_info(debugger_windows_interface &debugger, debugwin_info &owner, HWND parent); virtual ~memoryview_info(); + char const *expression() const; debug_view_memory::data_format data_format() const; uint32_t chunks_per_row() const; bool reverse() const; @@ -37,6 +40,9 @@ public: void set_physical(bool physical); void set_address_radix(int radix); + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; + protected: enum { @@ -51,4 +57,6 @@ private: std::string m_lastpc; }; +} // namespace osd::debugger::win + #endif // MAME_DEBUGGER_WIN_MEMORYVIEWINFO_H diff --git a/src/osd/modules/debugger/win/memorywininfo.cpp b/src/osd/modules/debugger/win/memorywininfo.cpp index 3ed9b6b9482..92078854e2e 100644 --- a/src/osd/modules/debugger/win/memorywininfo.cpp +++ b/src/osd/modules/debugger/win/memorywininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// memorywininfo.c - Win32 debug window handling +// memorywininfo.cpp - Win32 debug window handling // //============================================================ @@ -13,9 +13,13 @@ #include "memoryviewinfo.h" #include "uimetrics.h" +#include "util/xmlfile.h" + #include "winutf8.h" +namespace osd::debugger::win { + memorywin_info::memorywin_info(debugger_windows_interface &debugger) : editwin_info(debugger, false, "Memory", nullptr), m_combownd(nullptr) @@ -71,11 +75,11 @@ memorywin_info::memorywin_info(debugger_windows_interface &debugger) : update_caption(); // recompute the children once to get the maxwidth - memorywin_info::recompute_children(); + recompute_children(); // position the window and recompute children again - SetWindowPos(window(), HWND_TOP, 100, 100, maxwidth(), 200, SWP_SHOWWINDOW); - memorywin_info::recompute_children(); + debugger.stagger_window(window(), maxwidth(), 200); + recompute_children(); // mark the edit box as the default focus and set it editwin_info::set_default_focus(); @@ -388,3 +392,37 @@ void memorywin_info::update_caption() { win_set_window_text_utf8(window(), std::string("Memory: ").append(m_views[0]->source_name()).c_str()); } + + +void memorywin_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + m_views[0]->set_source_index(node.get_attribute_int(ATTR_WINDOW_MEMORY_REGION, m_views[0]->source_index())); + int const cursource = m_views[0]->source_index(); + if (0 <= cursource) + SendMessage(m_combownd, CB_SETCURSEL, cursource, 0); + update_caption(); + + util::xml::data_node const *const expr = node.get_child(NODE_WINDOW_EXPRESSION); + if (expr && expr->get_value()) + { + set_editwnd_text(expr->get_value()); + process_string(expr->get_value()); + } + + editwin_info::restore_configuration_from_node(node); + + m_views[0]->restore_configuration_from_node(node); +} + + +void memorywin_info::save_configuration_to_node(util::xml::data_node &node) +{ + editwin_info::save_configuration_to_node(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_MEMORY_VIEWER); + node.set_attribute_int(ATTR_WINDOW_MEMORY_REGION, m_views[0]->source_index()); + node.add_child(NODE_WINDOW_EXPRESSION, downcast<memoryview_info *>(m_views[0].get())->expression()); + m_views[0]->save_configuration_to_node(node); +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/memorywininfo.h b/src/osd/modules/debugger/win/memorywininfo.h index e0436605707..a0a4072be0f 100644 --- a/src/osd/modules/debugger/win/memorywininfo.h +++ b/src/osd/modules/debugger/win/memorywininfo.h @@ -15,6 +15,8 @@ #include "editwininfo.h" +namespace osd::debugger::win { + class memorywin_info : public editwin_info { public: @@ -22,12 +24,14 @@ public: virtual ~memorywin_info(); virtual bool handle_key(WPARAM wparam, LPARAM lparam) override; + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; protected: virtual void recompute_children() override; virtual void update_menu() override; virtual bool handle_command(WPARAM wparam, LPARAM lparam) override; virtual void draw_contents(HDC dc) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; private: virtual void process_string(const std::string &string) override; @@ -37,4 +41,6 @@ private: HWND m_combownd; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_MEMORYWININFO_H diff --git a/src/osd/modules/debugger/win/pointswininfo.cpp b/src/osd/modules/debugger/win/pointswininfo.cpp index 93926a697ae..f8fb4f6ce8d 100644 --- a/src/osd/modules/debugger/win/pointswininfo.cpp +++ b/src/osd/modules/debugger/win/pointswininfo.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// pointswininfo.c - Win32 debug window handling +// pointswininfo.cpp - Win32 debug window handling // //============================================================ @@ -11,9 +11,13 @@ #include "debugviewinfo.h" +#include "util/xmlfile.h" + #include "winutf8.h" +namespace osd::debugger::win { + pointswin_info::pointswin_info(debugger_windows_interface &debugger) : debugwin_info(debugger, false, std::string("All Breakpoints").c_str(), nullptr) { @@ -44,11 +48,9 @@ pointswin_info::pointswin_info(debugger_windows_interface &debugger) : // clamp the min/max size set_maxwidth(bounds.right - bounds.left); - // position the window at the bottom-right - SetWindowPos(window(), HWND_TOP, 100, 100, bounds.right - bounds.left, bounds.bottom - bounds.top, SWP_SHOWWINDOW); - - // recompute the children - debugwin_info::recompute_children(); + // position the window and recompute children + debugger.stagger_window(window(), bounds.right - bounds.left, bounds.bottom - bounds.top); + recompute_children(); } @@ -131,3 +133,46 @@ bool pointswin_info::handle_command(WPARAM wparam, LPARAM lparam) } return debugwin_info::handle_command(wparam, lparam); } + + +void pointswin_info::restore_configuration_from_node(util::xml::data_node const &node) +{ + switch (node.get_attribute_int(ATTR_WINDOW_POINTS_TYPE, -1)) + { + case 0: + SendMessage(window(), WM_COMMAND, ID_SHOW_BREAKPOINTS, 0); + break; + case 1: + SendMessage(window(), WM_COMMAND, ID_SHOW_WATCHPOINTS, 0); + break; + case 2: + SendMessage(window(), WM_COMMAND, ID_SHOW_REGISTERPOINTS, 0); + break; + } + + debugwin_info::restore_configuration_from_node(node); +} + + +void pointswin_info::save_configuration_to_node(util::xml::data_node &node) +{ + debugwin_info::save_configuration_to_node(node); + + node.set_attribute_int(ATTR_WINDOW_TYPE, WINDOW_TYPE_POINTS_VIEWER); + switch (m_views[0]->type()) + { + case DVT_BREAK_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 0); + break; + case DVT_WATCH_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 1); + break; + case DVT_REGISTER_POINTS: + node.set_attribute_int(ATTR_WINDOW_POINTS_TYPE, 2); + break; + default: + break; + } +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/pointswininfo.h b/src/osd/modules/debugger/win/pointswininfo.h index 4e36155105e..506c68519e8 100644 --- a/src/osd/modules/debugger/win/pointswininfo.h +++ b/src/osd/modules/debugger/win/pointswininfo.h @@ -15,6 +15,8 @@ #include "debugwininfo.h" +namespace osd::debugger::win { + class pointswin_info : public debugwin_info { public: @@ -22,10 +24,14 @@ public: virtual ~pointswin_info(); virtual bool handle_key(WPARAM wparam, LPARAM lparam) override; + virtual void restore_configuration_from_node(util::xml::data_node const &node) override; protected: virtual void update_menu() override; virtual bool handle_command(WPARAM wparam, LPARAM lparam) override; + virtual void save_configuration_to_node(util::xml::data_node &node) override; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_POINTSWININFO_H diff --git a/src/osd/modules/debugger/win/uimetrics.cpp b/src/osd/modules/debugger/win/uimetrics.cpp index 67eb045c619..e3d21cda06d 100644 --- a/src/osd/modules/debugger/win/uimetrics.cpp +++ b/src/osd/modules/debugger/win/uimetrics.cpp @@ -2,15 +2,48 @@ // copyright-holders:Aaron Giles, Vas Crabb //============================================================ // -// uimetrics.c - Win32 debug window handling +// uimetrics.cpp - Win32 debug window handling // //============================================================ #include "emu.h" #include "uimetrics.h" +#include "debug/debugvw.h" + #include "strconv.h" +#include <algorithm> + + +namespace osd::debugger::win { + +COLORREF const ui_metrics::s_themes[][COLOR_COUNT] = { + { + RGB(0x00, 0x00, 0x00), // foreground normal + RGB(0xff, 0x00, 0x00), // foreground changed + RGB(0x00, 0x00, 0xff), // foreground invalid + RGB(0x00, 0x80, 0x00), // foreground comment + RGB(0xff, 0xff, 0xff), // background normal + RGB(0xff, 0x80, 0x80), // background selected + RGB(0xe0, 0xe0, 0xe0), // background ancillary + RGB(0xff, 0xff, 0x00), // background current + RGB(0xff, 0xc0, 0x80), // background current selected + RGB(0xc0, 0xe0, 0xff) // background visited + }, + { + RGB(0xe0, 0xe0, 0xe0), // foreground normal + RGB(0xff, 0x60, 0xff), // foreground changed + RGB(0x00, 0xc0, 0xe0), // foreground invalid + RGB(0x00, 0xe0, 0x00), // foreground comment + RGB(0x00, 0x00, 0x00), // background normal + RGB(0xe0, 0x00, 0x00), // background selected + RGB(0x40, 0x40, 0x40), // background ancillary + RGB(0x00, 0x00, 0xc0), // background current + RGB(0xb0, 0x60, 0x00), // background current selected + RGB(0x00, 0x40, 0x80) // background visited + } }; + ui_metrics::ui_metrics(osd_options const &options) : m_debug_font(nullptr), @@ -22,7 +55,7 @@ ui_metrics::ui_metrics(osd_options const &options) : { // create a temporary DC HDC const temp_dc = GetDC(nullptr); - if (temp_dc != nullptr) + if (temp_dc) { float const size = options.debugger_font_size(); char const *const face = options.debugger_font(); @@ -47,6 +80,9 @@ ui_metrics::ui_metrics(osd_options const &options) : SelectObject(temp_dc, old_font); ReleaseDC(nullptr, temp_dc); } + + // set default color theme + set_color_theme(THEME_LIGHT_BACKGROUND); } @@ -69,3 +105,56 @@ ui_metrics::~ui_metrics() if (m_debug_font) DeleteObject(m_debug_font); } + + +std::pair<COLORREF, COLORREF> ui_metrics::view_colors(u8 attrib) const +{ + std::pair<COLORREF, COLORREF> result; + + if (attrib & DCA_SELECTED) + result.second = (attrib & DCA_CURRENT) ? m_colors[COLOR_BG_CURRENT_SELECTED] : m_colors[COLOR_BG_SELECTED]; + else if (attrib & DCA_CURRENT) + result.second = m_colors[COLOR_BG_CURRENT]; + else if (attrib & DCA_ANCILLARY) + result.second = m_colors[COLOR_BG_ANCILLARY]; + else if (attrib & DCA_VISITED) + result.second = m_colors[COLOR_BG_VISITED]; + else + result.second = m_colors[COLOR_BG_NORMAL]; + + if (DCA_COMMENT & attrib) + { + result.first = m_colors[COLOR_FG_COMMENT]; + } + else + { + if (attrib & DCA_INVALID) + result.first = m_colors[COLOR_FG_INVALID]; + else if (attrib & DCA_CHANGED) + result.first = m_colors[COLOR_FG_CHANGED]; + else + result.first = m_colors[COLOR_FG_NORMAL]; + + if (attrib & DCA_DISABLED) + { + result.first = RGB( + (GetRValue(result.first) + GetRValue(result.second) + 1) >> 1, + (GetGValue(result.first) + GetGValue(result.second) + 1) >> 1, + (GetBValue(result.first) + GetBValue(result.second) + 1) >> 1); + } + } + + return result; +} + + +void ui_metrics::set_color_theme(int index) +{ + if ((0 <= index) && (std::size(s_themes) > index)) + { + std::copy(std::begin(s_themes[index]), std::end(s_themes[index]), m_colors); + m_color_theme = index; + } +} + +} // namespace osd::debugger::win diff --git a/src/osd/modules/debugger/win/uimetrics.h b/src/osd/modules/debugger/win/uimetrics.h index 2687ada036c..e45ae810fa6 100644 --- a/src/osd/modules/debugger/win/uimetrics.h +++ b/src/osd/modules/debugger/win/uimetrics.h @@ -12,13 +12,22 @@ #include "debugwin.h" - #include "modules/lib/osdobj_common.h" +#include <utility> + + +namespace osd::debugger::win { class ui_metrics { public: + enum + { + THEME_LIGHT_BACKGROUND, + THEME_DARK_BACKGROUND + }; + ui_metrics(osd_options const &options); ui_metrics(ui_metrics const &that); ~ui_metrics(); @@ -31,7 +40,28 @@ public: uint32_t hscroll_height() const { return m_hscroll_height; } uint32_t vscroll_width() const { return m_vscroll_width; } + std::pair<COLORREF, COLORREF> view_colors(u8 attrib) const; + int get_color_theme() const { return m_color_theme; } + void set_color_theme(int index); + private: + enum + { + COLOR_FG_NORMAL, + COLOR_FG_CHANGED, + COLOR_FG_INVALID, + COLOR_FG_COMMENT, + + COLOR_BG_NORMAL, + COLOR_BG_SELECTED, + COLOR_BG_ANCILLARY, + COLOR_BG_CURRENT, + COLOR_BG_CURRENT_SELECTED, + COLOR_BG_VISITED, + + COLOR_COUNT + }; + HFONT m_debug_font; uint32_t m_debug_font_height; uint32_t m_debug_font_width; @@ -39,6 +69,13 @@ private: uint32_t const m_hscroll_height; uint32_t const m_vscroll_width; + + COLORREF m_colors[COLOR_COUNT]; + int m_color_theme; + + static COLORREF const s_themes[][COLOR_COUNT]; }; -#endif +} // namespace osd::debugger::win + +#endif // MAME_DEBUGGER_WIN_UIMETRICS_H diff --git a/src/osd/modules/debugger/xmlconfig.cpp b/src/osd/modules/debugger/xmlconfig.cpp new file mode 100644 index 00000000000..7e702938bf8 --- /dev/null +++ b/src/osd/modules/debugger/xmlconfig.cpp @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb + +#include "xmlconfig.h" + +namespace osd::debugger { + +char const *const NODE_WINDOW = "window"; +char const *const NODE_COLORS = "colors"; + +char const *const NODE_WINDOW_SPLITS = "splits"; +char const *const NODE_WINDOW_SELECTION = "selection"; +char const *const NODE_WINDOW_SCROLL = "scroll"; +char const *const NODE_WINDOW_EXPRESSION = "expression"; +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_WINDOW_TYPE = "type"; +char const *const ATTR_WINDOW_POSITION_X = "position_x"; +char const *const ATTR_WINDOW_POSITION_Y = "position_y"; +char const *const ATTR_WINDOW_WIDTH = "size_x"; +char const *const ATTR_WINDOW_HEIGHT = "size_y"; + +char const *const ATTR_WINDOW_MEMORY_REGION = "memoryregion"; +char const *const ATTR_WINDOW_MEMORY_REVERSE_COLUMNS = "reverse"; +char const *const ATTR_WINDOW_MEMORY_ADDRESS_MODE = "addressmode"; +char const *const ATTR_WINDOW_MEMORY_ADDRESS_RADIX = "addressradix"; +char const *const ATTR_WINDOW_MEMORY_DATA_FORMAT = "dataformat"; +char const *const ATTR_WINDOW_MEMORY_ROW_CHUNKS = "rowchunks"; + +char const *const ATTR_WINDOW_DISASSEMBLY_CPU = "cpu"; +char const *const ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN = "rightbar"; + +char const *const ATTR_WINDOW_POINTS_TYPE = "bwtype"; + +char const *const ATTR_WINDOW_DEVICE_TAG = "device-tag"; + +char const *const ATTR_COLORS_THEME = "theme"; + +char const *const ATTR_SPLITS_CONSOLE_STATE = "state"; +char const *const ATTR_SPLITS_CONSOLE_DISASSEMBLY = "disassembly"; + +char const *const ATTR_SELECTION_CURSOR_VISIBLE = "visible"; +char const *const ATTR_SELECTION_CURSOR_X = "start_x"; +char const *const ATTR_SELECTION_CURSOR_Y = "start_y"; + +char const *const ATTR_SCROLL_ORIGIN_X = "position_x"; +char const *const ATTR_SCROLL_ORIGIN_Y = "position_y"; + +} // namespace osd::debugger diff --git a/src/osd/modules/debugger/xmlconfig.h b/src/osd/modules/debugger/xmlconfig.h new file mode 100644 index 00000000000..ae5cdf8393c --- /dev/null +++ b/src/osd/modules/debugger/xmlconfig.h @@ -0,0 +1,71 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +#ifndef MAME_OSD_DEBUGGER_XMLCONFIG_H +#define MAME_OSD_DEBUGGER_XMLCONFIG_H + +#pragma once + +namespace osd::debugger { + +// Qt debugger started using these numeric types - they should be switched to mnemonics at some point +enum +{ + +WINDOW_TYPE_CONSOLE = 1, +WINDOW_TYPE_MEMORY_VIEWER, +WINDOW_TYPE_DISASSEMBLY_VIEWER, +WINDOW_TYPE_ERROR_LOG_VIEWER, +WINDOW_TYPE_POINTS_VIEWER, +WINDOW_TYPE_DEVICES_VIEWER, +WINDOW_TYPE_DEVICE_INFO_VIEWER + +}; + +extern char const *const NODE_WINDOW; +extern char const *const NODE_COLORS; + +extern char const *const NODE_WINDOW_SPLITS; +extern char const *const NODE_WINDOW_SELECTION; +extern char const *const NODE_WINDOW_SCROLL; +extern char const *const NODE_WINDOW_EXPRESSION; +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_WINDOW_TYPE; +extern char const *const ATTR_WINDOW_POSITION_X; +extern char const *const ATTR_WINDOW_POSITION_Y; +extern char const *const ATTR_WINDOW_WIDTH; +extern char const *const ATTR_WINDOW_HEIGHT; + +extern char const *const ATTR_WINDOW_MEMORY_REGION; +extern char const *const ATTR_WINDOW_MEMORY_REVERSE_COLUMNS; +extern char const *const ATTR_WINDOW_MEMORY_ADDRESS_MODE; +extern char const *const ATTR_WINDOW_MEMORY_ADDRESS_RADIX; +extern char const *const ATTR_WINDOW_MEMORY_DATA_FORMAT; +extern char const *const ATTR_WINDOW_MEMORY_ROW_CHUNKS; + +extern char const *const ATTR_WINDOW_DISASSEMBLY_CPU; +extern char const *const ATTR_WINDOW_DISASSEMBLY_RIGHT_COLUMN; + +extern char const *const ATTR_WINDOW_POINTS_TYPE; + +extern char const *const ATTR_WINDOW_DEVICE_TAG; + +extern char const *const ATTR_COLORS_THEME; + +extern char const *const ATTR_SPLITS_CONSOLE_STATE; +extern char const *const ATTR_SPLITS_CONSOLE_DISASSEMBLY; + +extern char const *const ATTR_SELECTION_CURSOR_VISIBLE; +extern char const *const ATTR_SELECTION_CURSOR_X; +extern char const *const ATTR_SELECTION_CURSOR_Y; + +extern char const *const ATTR_SCROLL_ORIGIN_X; +extern char const *const ATTR_SCROLL_ORIGIN_Y; + +} // namespace osd::debugger + +#endif // MAME_OSD_DEBUGGER_XMLCONFIG_H diff --git a/src/osd/modules/diagnostics/diagnostics_win32.cpp b/src/osd/modules/diagnostics/diagnostics_win32.cpp index b80bbdfcdba..660d4d91682 100644 --- a/src/osd/modules/diagnostics/diagnostics_win32.cpp +++ b/src/osd/modules/diagnostics/diagnostics_win32.cpp @@ -169,8 +169,8 @@ bool stack_walker::s_initialized = false; // stack_walker - constructor //------------------------------------------------- -stack_walker::stack_walker() - : m_process(GetCurrentProcess()), +stack_walker::stack_walker() : + m_process(GetCurrentProcess()), m_thread(GetCurrentThread()), m_first(true) { @@ -216,14 +216,18 @@ bool stack_walker::reset() m_stackframe.AddrStack.Mode = AddrModeFlat; // pull architecture-specific fields from the context -#ifdef PTR64 +#if defined(__x86_64__) || defined(_M_X64) m_stackframe.AddrPC.Offset = m_context.Rip; m_stackframe.AddrFrame.Offset = m_context.Rsp; m_stackframe.AddrStack.Offset = m_context.Rsp; -#else +#elif defined(__i386__) || defined(_M_IX86) m_stackframe.AddrPC.Offset = m_context.Eip; m_stackframe.AddrFrame.Offset = m_context.Ebp; m_stackframe.AddrStack.Offset = m_context.Esp; +#elif defined(__aarch64__) || defined(_M_ARM64) + m_stackframe.AddrPC.Offset = m_context.Pc; + m_stackframe.AddrFrame.Offset = m_context.Fp; + m_stackframe.AddrStack.Offset = m_context.Sp; #endif return true; } @@ -242,14 +246,18 @@ void stack_walker::reset(CONTEXT &initial, HANDLE thread) m_stackframe.AddrStack.Mode = AddrModeFlat; // pull architecture-specific fields from the context -#ifdef PTR64 +#if defined(__x86_64__) || defined(_M_X64) m_stackframe.AddrPC.Offset = m_context.Rip; m_stackframe.AddrFrame.Offset = m_context.Rsp; m_stackframe.AddrStack.Offset = m_context.Rsp; -#else +#elif defined(__i386__) || defined(_M_IX86) m_stackframe.AddrPC.Offset = m_context.Eip; m_stackframe.AddrFrame.Offset = m_context.Ebp; m_stackframe.AddrStack.Offset = m_context.Esp; +#elif defined(__aarch64__) || defined(_M_ARM64) + m_stackframe.AddrPC.Offset = m_context.Pc; + m_stackframe.AddrFrame.Offset = m_context.Fp; + m_stackframe.AddrStack.Offset = m_context.Sp; #endif } @@ -263,10 +271,14 @@ bool stack_walker::unwind() // if we were able to initialize, then we have everything we need if (s_initialized) { -#ifdef PTR64 +#if defined(__x86_64__) || defined(_M_X64) return (*m_stack_walk_64)(IMAGE_FILE_MACHINE_AMD64, m_process, m_thread, &m_stackframe, &m_context, nullptr, *m_sym_function_table_access_64, *m_sym_get_module_base_64, nullptr); -#else +#elif defined(__i386__) || defined(_M_IX86) return (*m_stack_walk_64)(IMAGE_FILE_MACHINE_I386, m_process, m_thread, &m_stackframe, &m_context, nullptr, *m_sym_function_table_access_64, *m_sym_get_module_base_64, nullptr); +#elif defined(__aarch64__) || defined(_M_ARM64) + return (*m_stack_walk_64)(IMAGE_FILE_MACHINE_ARM64, m_process, m_thread, &m_stackframe, &m_context, nullptr, *m_sym_function_table_access_64, *m_sym_get_module_base_64, nullptr); +#else + return false; #endif } @@ -289,8 +301,8 @@ bool stack_walker::unwind() // symbol_manager - constructor //------------------------------------------------- -symbol_manager::symbol_manager(const char *argv0) - : m_mapfile(argv0), +symbol_manager::symbol_manager(const char *argv0) : + m_mapfile(argv0), m_symfile(argv0), m_process(GetCurrentProcess()), m_last_base(0), @@ -300,7 +312,7 @@ symbol_manager::symbol_manager(const char *argv0) // compute the name of the mapfile int extoffs = m_mapfile.find_last_of('.'); if (extoffs != -1) - m_mapfile.substr(0, extoffs); + m_mapfile = m_mapfile.substr(0, extoffs); m_mapfile.append(".map"); // and the name of the symfile @@ -509,6 +521,9 @@ bool symbol_manager::parse_sym_line(const char *line, uintptr_t &address, std::s // first look for a (ty) entry const char *type = strstr(line, "(ty 20)"); if (type == nullptr) + type = strstr(line, "(ty 20)"); + + if (type == nullptr) return false; // scan forward in the line to find the address @@ -650,8 +665,8 @@ uintptr_t symbol_manager::get_text_section_base(ULONG &size) // sampling_profiler - constructor //------------------------------------------------- -sampling_profiler::sampling_profiler(uint32_t max_seconds, uint8_t stack_depth = 0) - : m_target_thread(nullptr), +sampling_profiler::sampling_profiler(uint32_t max_seconds, uint8_t stack_depth = 0) : + m_target_thread(nullptr), m_thread(nullptr), m_thread_id(0), m_thread_exit(false), @@ -994,7 +1009,7 @@ private: // print the state of the CPU fprintf(stderr, "-----------------------------------------------------\n"); -#ifdef PTR64 +#if defined(__x86_64__) || defined(_M_X64) fprintf(stderr, "RAX=%p RBX=%p RCX=%p RDX=%p\n", (void *)info->ContextRecord->Rax, (void *)info->ContextRecord->Rbx, @@ -1015,7 +1030,7 @@ private: (void *)info->ContextRecord->R13, (void *)info->ContextRecord->R14, (void *)info->ContextRecord->R15); -#else +#elif defined(__i386__) || defined(_M_IX86) fprintf(stderr, "EAX=%p EBX=%p ECX=%p EDX=%p\n", (void *)info->ContextRecord->Eax, (void *)info->ContextRecord->Ebx, @@ -1026,6 +1041,47 @@ private: (void *)info->ContextRecord->Edi, (void *)info->ContextRecord->Ebp, (void *)info->ContextRecord->Esp); +#elif defined(__aarch64__) || defined(_M_ARM64) + fprintf(stderr, " X0=%p X1=%p X2=%p X3=%p\n", + (void *)info->ContextRecord->X0, + (void *)info->ContextRecord->X1, + (void *)info->ContextRecord->X2, + (void *)info->ContextRecord->X3); + fprintf(stderr, " X4=%p X5=%p X6=%p X7=%p\n", + (void *)info->ContextRecord->X4, + (void *)info->ContextRecord->X5, + (void *)info->ContextRecord->X6, + (void *)info->ContextRecord->X7); + fprintf(stderr, " X8=%p X9=%p X10=%p X11=%p\n", + (void *)info->ContextRecord->X8, + (void *)info->ContextRecord->X9, + (void *)info->ContextRecord->X10, + (void *)info->ContextRecord->X11); + fprintf(stderr, "X12=%p X13=%p X14=%p X15=%p\n", + (void *)info->ContextRecord->X12, + (void *)info->ContextRecord->X13, + (void *)info->ContextRecord->X14, + (void *)info->ContextRecord->X15); + fprintf(stderr, "X16=%p X17=%p X18=%p X19=%p\n", + (void *)info->ContextRecord->X16, + (void *)info->ContextRecord->X17, + (void *)info->ContextRecord->X18, + (void *)info->ContextRecord->X19); + fprintf(stderr, "X20=%p X21=%p X22=%p X23=%p\n", + (void *)info->ContextRecord->X20, + (void *)info->ContextRecord->X21, + (void *)info->ContextRecord->X22, + (void *)info->ContextRecord->X23); + fprintf(stderr, "X24=%p X25=%p X26=%p X27=%p\n", + (void *)info->ContextRecord->X24, + (void *)info->ContextRecord->X25, + (void *)info->ContextRecord->X26, + (void *)info->ContextRecord->X27); + fprintf(stderr, "X28=%p FP=%p LR=%p SP=%p\n", + (void *)info->ContextRecord->X28, + (void *)info->ContextRecord->Fp, + (void *)info->ContextRecord->Lr, + (void *)info->ContextRecord->Sp); #endif // reprint the actual exception address diff --git a/src/osd/modules/file/posixdir.cpp b/src/osd/modules/file/posixdir.cpp index efe68c2eda9..282a58acfc9 100644 --- a/src/osd/modules/file/posixdir.cpp +++ b/src/osd/modules/file/posixdir.cpp @@ -193,7 +193,7 @@ bool posix_directory::open_impl(std::string const &dirname) { assert(!m_fd); - m_path = osd_subst_env(dirname); + m_path = dirname; m_fd.reset(::opendir(m_path.c_str())); return bool(m_fd); } diff --git a/src/osd/modules/file/posixfile.cpp b/src/osd/modules/file/posixfile.cpp index 3b0e7b4b537..c5ce8bf7b4e 100644 --- a/src/osd/modules/file/posixfile.cpp +++ b/src/osd/modules/file/posixfile.cpp @@ -257,8 +257,6 @@ std::error_condition osd_file::open(std::string const &path, std::uint32_t openf for (auto it = dst.begin(); it != dst.end(); ++it) *it = (INVPATHSEPCH == *it) ? PATHSEPCH : *it; #endif - try { dst = osd_subst_env(dst); } - catch (...) { return std::errc::not_enough_memory; } // attempt to open the file int fd = -1; diff --git a/src/osd/modules/file/winfile.cpp b/src/osd/modules/file/winfile.cpp index 199537bbdee..84c754cdd6f 100644 --- a/src/osd/modules/file/winfile.cpp +++ b/src/osd/modules/file/winfile.cpp @@ -62,12 +62,12 @@ public: LARGE_INTEGER largeOffset; largeOffset.QuadPart = offset; if (!SetFilePointerEx(m_handle, largeOffset, nullptr, FILE_BEGIN)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); // then perform the read DWORD result = 0; if (!ReadFile(m_handle, buffer, length, &result, nullptr)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); actual = result; return std::error_condition(); @@ -79,12 +79,12 @@ public: LARGE_INTEGER largeOffset; largeOffset.QuadPart = offset; if (!SetFilePointerEx(m_handle, largeOffset, nullptr, FILE_BEGIN)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); // then perform the write DWORD result = 0; if (!WriteFile(m_handle, buffer, length, &result, nullptr)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); actual = result; return std::error_condition(); @@ -96,11 +96,11 @@ public: LARGE_INTEGER largeOffset; largeOffset.QuadPart = offset; if (!SetFilePointerEx(m_handle, largeOffset, nullptr, FILE_BEGIN)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); // then perform the truncation if (!SetEndOfFile(m_handle)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); else return std::error_condition(); } @@ -161,12 +161,8 @@ DWORD create_path_recursive(TCHAR *path) // osd_open //============================================================ -std::error_condition osd_file::open(std::string const &orig_path, uint32_t openflags, ptr &file, std::uint64_t &filesize) noexcept +std::error_condition osd_file::open(std::string const &path, uint32_t openflags, ptr &file, std::uint64_t &filesize) noexcept { - std::string path; - try { path = osd_subst_env(orig_path); } - catch (...) { return std::errc::not_enough_memory; } - if (win_check_socket_path(path)) return win_open_socket(path, openflags, file, filesize); else if (win_check_ptty_path(path)) @@ -229,7 +225,7 @@ std::error_condition osd_file::open(std::string const &orig_path, uint32_t openf // if we still failed, clean up and free if (INVALID_HANDLE_VALUE == h) - return win_error_to_file_error(err); + return win_error_to_error_condition(err); } // get the file size @@ -259,7 +255,7 @@ std::error_condition osd_file::open(std::string const &orig_path, uint32_t openf if (NO_ERROR != err) { CloseHandle(h); - return win_error_to_file_error(err); + return win_error_to_error_condition(err); } } @@ -299,7 +295,7 @@ std::error_condition osd_file::remove(std::string const &filename) noexcept std::error_condition filerr; if (!DeleteFile(tempstr.c_str())) - filerr = win_error_to_file_error(GetLastError()); + filerr = win_error_to_error_condition(GetLastError()); return filerr; } @@ -415,12 +411,12 @@ std::error_condition osd_get_full_path(std::string &dst, std::string const &path std::wstring const w_path(osd::text::to_wstring(path)); DWORD const length(GetFullPathNameW(w_path.c_str(), 0, nullptr, nullptr)); if (!length) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); // allocate a buffer and get the canonical path std::unique_ptr<wchar_t []> buffer(std::make_unique<wchar_t []>(length)); if (!GetFullPathNameW(w_path.c_str(), length, buffer.get(), nullptr)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); // convert the result back to UTF-8 osd::text::from_wstring(dst, buffer.get()); @@ -534,69 +530,3 @@ bool osd_is_valid_filepath_char(char32_t uchar) noexcept && !(uchar >= '\x7F' && uchar <= '\x9F') && uchar_isvalid(uchar); } - - - -//============================================================ -// win_error_to_file_error -//============================================================ - -std::error_condition win_error_to_file_error(DWORD error) noexcept -{ - // TODO: work out if there's a better way to do this - switch (error) - { - case ERROR_SUCCESS: - return std::error_condition(); - - case ERROR_INVALID_HANDLE: - return std::errc::bad_file_descriptor; - - case ERROR_OUTOFMEMORY: - return std::errc::not_enough_memory; - - case ERROR_NOT_SUPPORTED: - return std::errc::not_supported; - - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - case ERROR_INVALID_NAME: - return std::errc::no_such_file_or_directory; - - case ERROR_FILENAME_EXCED_RANGE: - return std::errc::filename_too_long; - - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - return std::errc::permission_denied; - - case ERROR_ALREADY_EXISTS: - return std::errc::file_exists; - - case ERROR_TOO_MANY_OPEN_FILES: - return std::errc::too_many_files_open; - - case ERROR_WRITE_FAULT: - case ERROR_READ_FAULT: - return std::errc::io_error; - - case ERROR_HANDLE_DISK_FULL: - case ERROR_DISK_FULL: - return std::errc::no_space_on_device; - - case ERROR_PATH_BUSY: - case ERROR_BUSY: - return std::errc::device_or_resource_busy; - - case ERROR_FILE_TOO_LARGE: - return std::errc::file_too_large; - - case ERROR_INVALID_ACCESS: - case ERROR_NEGATIVE_SEEK: - case ERROR_BAD_ARGUMENTS: - return std::errc::invalid_argument; - - default: - return std::error_condition(error, std::system_category()); - } -} diff --git a/src/osd/modules/file/winfile.h b/src/osd/modules/file/winfile.h index 83f0814733d..90cf6e69a60 100644 --- a/src/osd/modules/file/winfile.h +++ b/src/osd/modules/file/winfile.h @@ -32,6 +32,4 @@ std::error_condition win_open_socket(std::string const &path, std::uint32_t open bool win_check_ptty_path(std::string const &path) noexcept; std::error_condition win_open_ptty(std::string const &path, std::uint32_t openflags, osd_file::ptr &file, std::uint64_t &filesize) noexcept; -std::error_condition win_error_to_file_error(DWORD error) noexcept; - #endif // MAME_OSD_MODULES_FILE_WINFILE_H diff --git a/src/osd/modules/file/winptty.cpp b/src/osd/modules/file/winptty.cpp index 7feeacce137..9aac0c5a990 100644 --- a/src/osd/modules/file/winptty.cpp +++ b/src/osd/modules/file/winptty.cpp @@ -8,11 +8,11 @@ #include "winutil.h" #include <cassert> - -#include <windows.h> #include <cstdlib> #include <cstring> +#include <windows.h> + namespace { @@ -44,7 +44,7 @@ public: { DWORD bytes_read; if (!ReadFile(m_handle, buffer, count, &bytes_read, nullptr)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); actual = bytes_read; return std::error_condition(); @@ -54,7 +54,7 @@ public: { DWORD bytes_written; if (!WriteFile(m_handle, buffer, count, &bytes_written, nullptr)) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); actual = bytes_written; return std::error_condition(); @@ -99,7 +99,7 @@ std::error_condition win_open_ptty(std::string const &path, std::uint32_t openfl if (openflags & OPEN_FLAG_CREATE) pipe = CreateNamedPipe(t_name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 32, 32, 0, nullptr); if (INVALID_HANDLE_VALUE == pipe) - return win_error_to_file_error(GetLastError()); + return win_error_to_error_condition(GetLastError()); } else { diff --git a/src/osd/modules/file/winrtfile.cpp b/src/osd/modules/file/winrtfile.cpp index 3de1c19605d..5053f6e0199 100644 --- a/src/osd/modules/file/winrtfile.cpp +++ b/src/osd/modules/file/winrtfile.cpp @@ -160,12 +160,8 @@ DWORD create_path_recursive(TCHAR *path) // osd_open //============================================================ -osd_file::error osd_file::open(std::string const &orig_path, uint32_t openflags, ptr &file, std::uint64_t &filesize) +osd_file::error osd_file::open(std::string const &path, uint32_t openflags, ptr &file, std::uint64_t &filesize) { - std::string path; - try { path = osd_subst_env(orig_path); } - catch (...) { return std::errc::not_enough_memory; } - if (win_check_socket_path(path)) return win_open_socket(path, openflags, file, filesize); else if (win_check_ptty_path(path)) diff --git a/src/osd/modules/font/font_dwrite.cpp b/src/osd/modules/font/font_dwrite.cpp index 70edc24c0f1..ec152fb1b0d 100644 --- a/src/osd/modules/font/font_dwrite.cpp +++ b/src/osd/modules/font/font_dwrite.cpp @@ -6,17 +6,24 @@ */ #include "font_module.h" -#include "modules/osdmodule.h" + #include "modules/lib/osdlib.h" #if defined(OSD_WINDOWS) -#include <windows.h> +#include "corestr.h" + +#include "winutil.h" + +#include "osdcore.h" +#include "strconv.h" #include <cmath> #include <memory> #include <stdexcept> +#include <windows.h> + // Windows Imaging Components #include <wincodec.h> @@ -34,10 +41,10 @@ DEFINE_GUID(GUID_WICPixelFormat8bppAlpha, 0xe6cd0116, 0xeeba, 0x4161, 0xaa, 0x85 #include <wrl/client.h> #undef interface -#include "strconv.h" -#include "corestr.h" -#include "winutil.h" -#include "osdcore.h" + +namespace osd { + +namespace { using namespace Microsoft::WRL; @@ -361,7 +368,7 @@ public: bool italic = (strreplace(name, "[I]", "") + strreplace(name, "[i]", "") > 0); // convert the face name - std::wstring familyName = osd::text::to_wstring(name.c_str()); + std::wstring familyName = text::to_wstring(name); // find the font HR_RET0(find_font( @@ -623,7 +630,7 @@ private: HR_RETHR(fonts->FindFamilyName(familyName.c_str(), &family_index, &exists)); if (!exists) { - osd_printf_error("Font with family name %s does not exist.\n", osd::text::from_wstring(familyName)); + osd_printf_error("Font with family name %s does not exist.\n", text::from_wstring(familyName)); return E_FAIL; } @@ -674,7 +681,7 @@ public: return true; } - virtual int init(const osd_options &options) override + virtual int init(osd_interface &osd, const osd_options &options) override { HRESULT result; @@ -742,7 +749,7 @@ public: std::unique_ptr<WCHAR[]> name = nullptr; HR_RET0(get_localized_familyname(names, name)); - std::string utf8_name = osd::text::from_wstring(name.get()); + std::string utf8_name = text::from_wstring(name.get()); name.reset(); // Review: should the config name, be unlocalized? @@ -806,8 +813,15 @@ private: } }; +} // anonymous namespace + +} // namespace osd + #else -MODULE_NOT_SUPPORTED(font_dwrite, OSD_FONT_PROVIDER, "dwrite") + +namespace osd { namespace { MODULE_NOT_SUPPORTED(font_dwrite, OSD_FONT_PROVIDER, "dwrite") } } + #endif -MODULE_DEFINITION(FONT_DWRITE, font_dwrite) + +MODULE_DEFINITION(FONT_DWRITE, osd::font_dwrite) diff --git a/src/osd/modules/font/font_module.h b/src/osd/modules/font/font_module.h index 81ffaca74e5..7fe04e32628 100644 --- a/src/osd/modules/font/font_module.h +++ b/src/osd/modules/font/font_module.h @@ -24,7 +24,7 @@ class font_module { public: - virtual ~font_module() { } + virtual ~font_module() = default; /** attempt to allocate a font instance */ virtual osd_font::ptr font_alloc() = 0; diff --git a/src/osd/modules/font/font_none.cpp b/src/osd/modules/font/font_none.cpp index dc6116625a8..abf56007b00 100644 --- a/src/osd/modules/font/font_none.cpp +++ b/src/osd/modules/font/font_none.cpp @@ -4,9 +4,12 @@ * font_none.c * */ - #include "font_module.h" -#include "modules/osdmodule.h" + + +namespace osd { + +namespace { class osd_font_none : public osd_font { @@ -23,10 +26,15 @@ class font_none : public osd_module, public font_module public: font_none() : osd_module(OSD_FONT_PROVIDER, "none"), font_module() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual osd_font::ptr font_alloc() override { return std::make_unique<osd_font_none>(); } virtual bool get_font_families(std::string const &font_path, std::vector<std::pair<std::string, std::string> > &result) override { return false; } }; -MODULE_DEFINITION(FONT_NONE, font_none) +} // anonymous namespace + +} // namespace osd + + +MODULE_DEFINITION(FONT_NONE, osd::font_none) diff --git a/src/osd/modules/font/font_osx.cpp b/src/osd/modules/font/font_osx.cpp index cde01f58b69..a020d51ddce 100644 --- a/src/osd/modules/font/font_osx.cpp +++ b/src/osd/modules/font/font_osx.cpp @@ -6,7 +6,6 @@ */ #include "font_module.h" -#include "modules/osdmodule.h" #ifdef SDLMAME_MACOSX @@ -212,7 +211,7 @@ class font_osx : public osd_module, public font_module public: font_osx() : osd_module(OSD_FONT_PROVIDER, "osx"), font_module() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual osd_font::ptr font_alloc() override { return std::make_unique<osd_font_osx>(); } virtual bool get_font_families(std::string const &font_path, std::vector<std::pair<std::string, std::string> > &result) override; diff --git a/src/osd/modules/font/font_sdl.cpp b/src/osd/modules/font/font_sdl.cpp index 6c97f0a71f0..1938c97a44d 100644 --- a/src/osd/modules/font/font_sdl.cpp +++ b/src/osd/modules/font/font_sdl.cpp @@ -6,7 +6,6 @@ */ #include "font_module.h" -#include "modules/osdmodule.h" #if defined(SDLMAME_UNIX) && !defined(SDLMAME_MACOSX) && !defined(SDLMAME_HAIKU) && !defined(SDLMAME_ANDROID) @@ -200,9 +199,8 @@ osd_font_sdl::TTF_Font_ptr osd_font_sdl::TTF_OpenFont_Magic(std::string const &n unsigned char const ttf_magic[] = { 0x00, 0x01, 0x00, 0x00, 0x00 }; unsigned char const ttc1_magic[] = { 0x74, 0x74, 0x63, 0x66, 0x00, 0x01, 0x00, 0x00 }; unsigned char const ttc2_magic[] = { 0x74, 0x74, 0x63, 0x66, 0x00, 0x02, 0x00, 0x00 }; - auto buffer_size = std::max({ sizeof(ttf_magic), sizeof(ttc1_magic), sizeof(ttc2_magic) }); - unsigned char buffer[buffer_size]; - auto const bytes_read = file.read(buffer, buffer_size); + unsigned char buffer[std::max({ sizeof(ttf_magic), sizeof(ttc1_magic), sizeof(ttc2_magic) })]; + auto const bytes_read = file.read(buffer, std::size(buffer)); file.close(); if (((bytes_read >= sizeof(ttf_magic)) && !std::memcmp(buffer, ttf_magic, sizeof(ttf_magic))) || @@ -305,7 +303,7 @@ public: return std::make_unique<osd_font_sdl>(); } - virtual int init(const osd_options &options) override + virtual int init(osd_interface &osd, const osd_options &options) override { if (TTF_Init() == -1) { @@ -351,10 +349,10 @@ bool font_sdl::get_font_families(std::string const &font_path, std::vector<std:: { auto const compare_fonts = [](std::pair<std::string, std::string> const &a, std::pair<std::string, std::string> const &b) -> bool { - int const second = core_stricmp(a.second.c_str(), b.second.c_str()); + int const second = core_stricmp(a.second, b.second); if (second < 0) return true; else if (second > 0) return false; - else return core_stricmp(b.first.c_str(), b.first.c_str()) < 0; + else return core_stricmp(b.first, b.first) < 0; }; std::string config((const char *)val.u.s); std::string display(config); diff --git a/src/osd/modules/font/font_windows.cpp b/src/osd/modules/font/font_windows.cpp index 8e0bd568349..0ec65b67aea 100644 --- a/src/osd/modules/font/font_windows.cpp +++ b/src/osd/modules/font/font_windows.cpp @@ -7,7 +7,6 @@ #include "font_module.h" -#include "modules/osdmodule.h" #if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) @@ -25,6 +24,8 @@ #include <io.h> +namespace osd { + namespace { class osd_font_windows : public osd_font @@ -83,7 +84,7 @@ bool osd_font_windows::open(std::string const &font_path, std::string const &_na logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; // copy in the face name - osd::text::tstring face = osd::text::to_tstring(name); + text::tstring face = text::to_tstring(name); _tcsncpy(logfont.lfFaceName, face.c_str(), std::size(logfont.lfFaceName)); logfont.lfFaceName[sizeof(logfont.lfFaceName) / sizeof(TCHAR)-1] = 0; @@ -108,8 +109,8 @@ bool osd_font_windows::open(std::string const &font_path, std::string const &_na } // if it doesn't match our request, fail - std::string utf = osd::text::from_tstring(&realname[0]); - int result = core_stricmp(utf.c_str(), name.c_str()); + std::string utf = text::from_tstring(&realname[0]); + int result = core_stricmp(utf, name); // if we didn't match, nuke our font and fall back if (result != 0) @@ -288,7 +289,7 @@ class font_win : public osd_module, public font_module public: font_win() : osd_module(OSD_FONT_PROVIDER, "win"), font_module() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual osd_font::ptr font_alloc() override { return std::make_unique<osd_font_windows>(); } @@ -298,7 +299,7 @@ private: static int CALLBACK font_family_callback(LOGFONT const *lpelfe, TEXTMETRIC const *lpntme, DWORD FontType, LPARAM lParam) { auto &result = *reinterpret_cast<std::vector<std::pair<std::string, std::string> > *>(lParam); - std::string face = osd::text::from_tstring(lpelfe->lfFaceName); + std::string face = text::from_tstring(lpelfe->lfFaceName); if ((face[0] != '@') && (result.empty() || (result.back().first != face))) result.emplace_back(face, face); return TRUE; } @@ -326,10 +327,12 @@ bool font_win::get_font_families(std::string const &font_path, std::vector<std:: } // anonymous namespace +} // namespace osd + #else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -MODULE_NOT_SUPPORTED(font_win, OSD_FONT_PROVIDER, "win") +namespace osd { namespace { MODULE_NOT_SUPPORTED(font_win, OSD_FONT_PROVIDER, "win") } } #endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -MODULE_DEFINITION(FONT_WINDOWS, font_win) +MODULE_DEFINITION(FONT_WINDOWS, osd::font_win) diff --git a/src/osd/modules/input/assignmenthelper.cpp b/src/osd/modules/input/assignmenthelper.cpp new file mode 100644 index 00000000000..c044d4b7aa7 --- /dev/null +++ b/src/osd/modules/input/assignmenthelper.cpp @@ -0,0 +1,592 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +//============================================================ +// +// assignmenthelper.cpp - input assignment setup helper +// +//============================================================ + +#include "assignmenthelper.h" + +#include "interface/inputseq.h" + +#include "inpttype.h" + + +namespace osd { + +bool joystick_assignment_helper::add_assignment( + input_device::assignment_vector &assignments, + ioport_type fieldtype, + input_seq_type seqtype, + input_item_class itemclass, + input_item_modifier modifier, + std::initializer_list<input_item_id> items) +{ + for (input_item_id item : items) + { + if (ITEM_ID_INVALID != item) + { + assignments.emplace_back( + fieldtype, + seqtype, + input_seq(input_code(DEVICE_CLASS_JOYSTICK, 0, itemclass, modifier, item))); + return true; + } + } + return false; +} + + +bool joystick_assignment_helper::add_button_assignment( + input_device::assignment_vector &assignments, + ioport_type field_type, + std::initializer_list<input_item_id> items) +{ + return add_assignment( + assignments, + field_type, + SEQ_TYPE_STANDARD, + ITEM_CLASS_SWITCH, + ITEM_MODIFIER_NONE, + items); +} + + +bool joystick_assignment_helper::add_button_pair_assignment( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id button1, + input_item_id button2) +{ + if ((ITEM_ID_INVALID == button1) || (ITEM_ID_INVALID == button2)) + return false; + + assignments.emplace_back( + field1, + SEQ_TYPE_STANDARD, + make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button1)); + assignments.emplace_back( + field2, + SEQ_TYPE_STANDARD, + make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button2)); + return true; +} + + +bool joystick_assignment_helper::add_axis_inc_dec_assignment( + input_device::assignment_vector &assignments, + ioport_type field_type, + input_item_id button_dec, + input_item_id button_inc) +{ + if ((ITEM_ID_INVALID == button_dec) || (ITEM_ID_INVALID == button_inc)) + return false; + + assignments.emplace_back( + field_type, + SEQ_TYPE_DECREMENT, + make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button_dec)); + assignments.emplace_back( + field_type, + SEQ_TYPE_INCREMENT, + make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, button_inc)); + return true; +} + + +bool joystick_assignment_helper::add_axis_pair_assignment( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id axis) +{ + if (ITEM_ID_INVALID == axis) + return false; + + assignments.emplace_back( + field1, + SEQ_TYPE_STANDARD, + make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_XAXIS == axis) ? ITEM_MODIFIER_LEFT : (ITEM_ID_YAXIS == axis) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG, + axis)); + assignments.emplace_back( + field2, + SEQ_TYPE_STANDARD, + make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_XAXIS == axis) ? ITEM_MODIFIER_RIGHT : (ITEM_ID_YAXIS == axis) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS, + axis)); + return true; +} + + +bool joystick_assignment_helper::consume_button_pair( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id &button1, + input_item_id &button2) +{ + if (!add_button_pair_assignment(assignments, field1, field2, button1, button2)) + return false; + + button1 = ITEM_ID_INVALID; + button2 = ITEM_ID_INVALID; + return true; +} + + +bool joystick_assignment_helper::consume_trigger_pair( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id &axis1, + input_item_id &axis2) +{ + if ((ITEM_ID_INVALID == axis1) || (ITEM_ID_INVALID == axis2)) + return false; + + assignments.emplace_back( + field1, + SEQ_TYPE_STANDARD, + make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axis1)); + assignments.emplace_back( + field2, + SEQ_TYPE_STANDARD, + make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axis2)); + axis1 = ITEM_ID_INVALID; + axis2 = ITEM_ID_INVALID; + return true; +} + + +bool joystick_assignment_helper::consume_axis_pair( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id &axis) +{ + if (!add_axis_pair_assignment(assignments, field1, field2, axis)) + return false; + + axis = ITEM_ID_INVALID; + return true; +} + + +void joystick_assignment_helper::add_directional_assignments( + input_device::assignment_vector &assignments, + input_item_id xaxis, + input_item_id yaxis, + input_item_id leftswitch, + input_item_id rightswitch, + input_item_id upswitch, + input_item_id downswitch) +{ + // see if we have complementary pairs of directional switches + bool const hswitches = (ITEM_ID_INVALID != leftswitch) && (ITEM_ID_INVALID != rightswitch); + bool const vswitches = (ITEM_ID_INVALID != upswitch) && (ITEM_ID_INVALID != downswitch); + + // use X axis if present + if (ITEM_ID_INVALID != xaxis) + { + // use this for horizontal axis movement + input_seq const xseq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, xaxis)); + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, xseq); + + // use it for the main left/right control, too + input_seq leftseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_XAXIS == xaxis) ? ITEM_MODIFIER_LEFT : ITEM_MODIFIER_NEG, xaxis)); + input_seq rightseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_XAXIS == xaxis) ? ITEM_MODIFIER_RIGHT : ITEM_MODIFIER_POS, xaxis)); + if (ITEM_ID_INVALID != leftswitch) + { + leftseq += input_seq::or_code; + leftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftswitch); + } + if (ITEM_ID_INVALID != rightswitch) + { + rightseq += input_seq::or_code; + rightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightswitch); + } + assignments.emplace_back(IPT_JOYSTICK_LEFT, SEQ_TYPE_STANDARD, leftseq); + assignments.emplace_back(IPT_JOYSTICK_RIGHT, SEQ_TYPE_STANDARD, rightseq); + + // use for vertical navigation if there's no Y axis, or horizontal otherwise + if (ITEM_ID_INVALID != yaxis) + { + // if left/right are both present but not both up/down, they'll be taken for vertical navigation + if (hswitches && !vswitches) + { + leftseq.backspace(); + if (ITEM_ID_INVALID != upswitch) + leftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch); + else + leftseq.backspace(); + + rightseq.backspace(); + if (ITEM_ID_INVALID != downswitch) + rightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch); + else + rightseq.backspace(); + } + assignments.emplace_back(IPT_UI_LEFT, SEQ_TYPE_STANDARD, leftseq); + assignments.emplace_back(IPT_UI_RIGHT, SEQ_TYPE_STANDARD, rightseq); + } + else + { + // prefer D-pad up/down for vertical navigation if present + if (!hswitches || vswitches) + { + while (leftseq.length() > 1) + leftseq.backspace(); + if (ITEM_ID_INVALID != upswitch) + { + leftseq += input_seq::or_code; + leftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch); + } + + while (rightseq.length() > 1) + rightseq.backspace(); + if (ITEM_ID_INVALID != downswitch) + { + rightseq += input_seq::or_code; + rightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch); + } + } + assignments.emplace_back(IPT_UI_UP, SEQ_TYPE_STANDARD, leftseq); + assignments.emplace_back(IPT_UI_DOWN, SEQ_TYPE_STANDARD, rightseq); + } + } + else + { + // without a primary analog X axis, we still want D-pad left/right controls if possible + add_button_assignment(assignments, IPT_JOYSTICK_LEFT, { leftswitch }); + add_button_assignment(assignments, IPT_JOYSTICK_RIGHT, { rightswitch }); + + // vertical navigation gets first pick on directional controls + add_button_assignment(assignments, IPT_UI_LEFT, { (hswitches && !vswitches) ? upswitch : leftswitch }); + add_button_assignment(assignments, IPT_UI_RIGHT, { (hswitches && !vswitches) ? downswitch : rightswitch }); + } + + // use Y axis if present + if (ITEM_ID_INVALID != yaxis) + { + // use this for vertical axis movement + input_seq const yseq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, yaxis)); + assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, yseq); + + // use it for the main up/down control, too + input_seq upseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_YAXIS == yaxis) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG, yaxis)); + input_seq downseq(make_code(ITEM_CLASS_SWITCH, (ITEM_ID_YAXIS == yaxis) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS, yaxis)); + if (ITEM_ID_INVALID != upswitch) + { + upseq += input_seq::or_code; + upseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch); + } + if (ITEM_ID_INVALID != downswitch) + { + downseq += input_seq::or_code; + downseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch); + } + assignments.emplace_back(IPT_JOYSTICK_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_JOYSTICK_DOWN, SEQ_TYPE_STANDARD, downseq); + + // if available, this is used for vertical navigation + if (hswitches && !vswitches) + { + if (upseq.length() > 1) + upseq.backspace(); + else + upseq += input_seq::or_code; + upseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftswitch); + + if (downseq.length() > 1) + downseq.backspace(); + else + downseq += input_seq::or_code; + downseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightswitch); + } + assignments.emplace_back(IPT_UI_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_UI_DOWN, SEQ_TYPE_STANDARD, downseq); + } + else + { + // without a primary analog Y axis, we still want D-pad up/down controls if possible + add_button_assignment(assignments, IPT_JOYSTICK_UP, { upswitch }); + add_button_assignment(assignments, IPT_JOYSTICK_DOWN, { downswitch }); + + // vertical navigation may be assigned to X axis if Y axis is not present + bool const dpadflip = (ITEM_ID_INVALID != xaxis) == (!hswitches || vswitches); + add_button_assignment( + assignments, + (ITEM_ID_INVALID != xaxis) ? IPT_UI_LEFT : IPT_UI_UP, + { dpadflip ? leftswitch : upswitch }); + add_button_assignment( + assignments, + (ITEM_ID_INVALID != xaxis) ? IPT_UI_RIGHT : IPT_UI_DOWN, + { dpadflip ? rightswitch : downswitch }); + } + + // if we're missing either primary axis, fall back to D-pad for analog increment/decrement + if ((ITEM_ID_INVALID == xaxis) || (ITEM_ID_INVALID == yaxis)) + { + if (ITEM_ID_INVALID != leftswitch) + { + input_seq const leftseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftswitch)); + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_DECREMENT, leftseq); + assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_DECREMENT, leftseq); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_DECREMENT, leftseq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_DECREMENT, leftseq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_DECREMENT, leftseq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_DECREMENT, leftseq); + } + if (ITEM_ID_INVALID != rightswitch) + { + input_seq const rightseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightswitch)); + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_INCREMENT, rightseq); + assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_INCREMENT, rightseq); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_INCREMENT, rightseq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_INCREMENT, rightseq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_INCREMENT, rightseq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_INCREMENT, rightseq); + } + if (ITEM_ID_INVALID != upswitch) + { + input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, upswitch)); + assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_DECREMENT, upseq); + assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_DECREMENT, upseq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_DECREMENT, upseq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_DECREMENT, upseq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_DECREMENT, upseq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_DECREMENT, upseq); + } + if (ITEM_ID_INVALID != downswitch) + { + input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, downswitch)); + assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_INCREMENT, downseq); + assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_INCREMENT, downseq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_INCREMENT, downseq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_INCREMENT, downseq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_INCREMENT, downseq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_INCREMENT, downseq); + } + } +} + + +void joystick_assignment_helper::add_twin_stick_assignments( + input_device::assignment_vector &assignments, + input_item_id leftx, + input_item_id lefty, + input_item_id rightx, + input_item_id righty, + input_item_id leftleft, + input_item_id leftright, + input_item_id leftup, + input_item_id leftdown, + input_item_id rightleft, + input_item_id rightright, + input_item_id rightup, + input_item_id rightdown) +{ + // we'll add these at the end if they aren't empty + input_seq leftleftseq, leftrightseq, leftupseq, leftdownseq; + input_seq rightleftseq, rightrightseq, rightupseq, rightdownseq; + + // only use axes if there are at least two axes in the same orientation + bool const useaxes = + ((ITEM_ID_INVALID != leftx) && (ITEM_ID_INVALID != rightx)) || + ((ITEM_ID_INVALID != lefty) && (ITEM_ID_INVALID != righty)); + if (useaxes) + { + // left stick + if (ITEM_ID_INVALID != leftx) + { + leftleftseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_XAXIS == leftx) ? ITEM_MODIFIER_LEFT : ITEM_MODIFIER_NEG, + leftx); + leftrightseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_XAXIS == leftx) ? ITEM_MODIFIER_RIGHT : ITEM_MODIFIER_POS, + leftx); + } + if (ITEM_ID_INVALID != lefty) + { + leftupseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_YAXIS == lefty) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG, + lefty); + leftdownseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_YAXIS == lefty) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS, + lefty); + } + + // right stick + if (ITEM_ID_INVALID != rightx) + { + rightleftseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_XAXIS == rightx) ? ITEM_MODIFIER_LEFT : ITEM_MODIFIER_NEG, + rightx); + rightrightseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_XAXIS == rightx) ? ITEM_MODIFIER_RIGHT : ITEM_MODIFIER_POS, + rightx); + } + if (ITEM_ID_INVALID != righty) + { + rightupseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_YAXIS == righty) ? ITEM_MODIFIER_UP : ITEM_MODIFIER_NEG, + righty); + rightdownseq += make_code( + ITEM_CLASS_SWITCH, + (ITEM_ID_YAXIS == righty) ? ITEM_MODIFIER_DOWN : ITEM_MODIFIER_POS, + righty); + } + } + + // only use switches if we have at least one pair of matching opposing directions + bool const lefth = (ITEM_ID_INVALID != leftleft) && (ITEM_ID_INVALID != leftright); + bool const leftv = (ITEM_ID_INVALID != leftup) && (ITEM_ID_INVALID != leftdown); + bool const righth = (ITEM_ID_INVALID != rightleft) && (ITEM_ID_INVALID != rightright); + bool const rightv = (ITEM_ID_INVALID != rightup) && (ITEM_ID_INVALID != rightdown); + if ((lefth && righth) || (leftv && rightv)) + { + // left stick + if (ITEM_ID_INVALID != leftleft) + { + if (!leftleftseq.empty()) + leftleftseq += input_seq::or_code; + leftleftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftleft); + } + if (ITEM_ID_INVALID != leftright) + { + if (!leftrightseq.empty()) + leftrightseq += input_seq::or_code; + leftrightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftright); + } + if (ITEM_ID_INVALID != leftup) + { + if (!leftupseq.empty()) + leftupseq += input_seq::or_code; + leftupseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftup); + } + if (ITEM_ID_INVALID != leftdown) + { + if (!leftdownseq.empty()) + leftdownseq += input_seq::or_code; + leftdownseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, leftdown); + } + + // right stick + if (ITEM_ID_INVALID != rightleft) + { + if (!rightleftseq.empty()) + rightleftseq += input_seq::or_code; + rightleftseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightleft); + } + if (ITEM_ID_INVALID != rightright) + { + if (!rightrightseq.empty()) + rightrightseq += input_seq::or_code; + rightrightseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightright); + } + if (ITEM_ID_INVALID != rightup) + { + if (!rightupseq.empty()) + rightupseq += input_seq::or_code; + rightupseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightup); + } + if (ITEM_ID_INVALID != rightdown) + { + if (!rightdownseq.empty()) + rightdownseq += input_seq::or_code; + rightdownseq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, rightdown); + } + } + + // now add collected assignments + if (!leftleftseq.empty()) + assignments.emplace_back(IPT_JOYSTICKLEFT_LEFT, SEQ_TYPE_STANDARD, leftleftseq); + if (!leftrightseq.empty()) + assignments.emplace_back(IPT_JOYSTICKLEFT_RIGHT, SEQ_TYPE_STANDARD, leftrightseq); + if (!leftupseq.empty()) + assignments.emplace_back(IPT_JOYSTICKLEFT_UP, SEQ_TYPE_STANDARD, leftupseq); + if (!leftdownseq.empty()) + assignments.emplace_back(IPT_JOYSTICKLEFT_DOWN, SEQ_TYPE_STANDARD, leftdownseq); + if (!rightleftseq.empty()) + assignments.emplace_back(IPT_JOYSTICKRIGHT_LEFT, SEQ_TYPE_STANDARD, rightleftseq); + if (!rightrightseq.empty()) + assignments.emplace_back(IPT_JOYSTICKRIGHT_RIGHT, SEQ_TYPE_STANDARD, rightrightseq); + if (!rightupseq.empty()) + assignments.emplace_back(IPT_JOYSTICKRIGHT_UP, SEQ_TYPE_STANDARD, rightupseq); + if (!rightdownseq.empty()) + assignments.emplace_back(IPT_JOYSTICKRIGHT_DOWN, SEQ_TYPE_STANDARD, rightdownseq); +} + + +void joystick_assignment_helper::choose_primary_stick( + input_item_id (&stickaxes)[2][2], + input_item_id leftx, + input_item_id lefty, + input_item_id rightx, + input_item_id righty) +{ + if ((ITEM_ID_INVALID != leftx) && (ITEM_ID_INVALID != lefty)) + { + // left stick has both axes, make it primary + stickaxes[0][0] = leftx; + stickaxes[0][1] = lefty; + stickaxes[1][0] = rightx; + stickaxes[1][1] = righty; + } + else if ((ITEM_ID_INVALID != rightx) && (ITEM_ID_INVALID != righty)) + { + // right stick has both axes, make it primary + stickaxes[0][0] = rightx; + stickaxes[0][1] = righty; + stickaxes[1][0] = leftx; + stickaxes[1][1] = lefty; + } + else if (ITEM_ID_INVALID != leftx) + { + // degenerate case - left X and possibly right X or Y + stickaxes[0][0] = leftx; + stickaxes[0][1] = righty; + stickaxes[1][0] = rightx; + stickaxes[1][1] = ITEM_ID_INVALID; + } + else if ((ITEM_ID_INVALID != rightx) || (ITEM_ID_INVALID != lefty)) + { + // degenerate case - right X and possibly left Y, or one or two Y axes + stickaxes[0][0] = rightx; + stickaxes[0][1] = lefty; + stickaxes[1][0] = ITEM_ID_INVALID; + stickaxes[1][1] = righty; + } + else + { + // degenerate case - one Y axis at most + stickaxes[0][0] = ITEM_ID_INVALID; + stickaxes[0][1] = righty; + stickaxes[1][0] = ITEM_ID_INVALID; + stickaxes[1][1] = ITEM_ID_INVALID; + } +} + +} // namespace osd diff --git a/src/osd/modules/input/assignmenthelper.h b/src/osd/modules/input/assignmenthelper.h new file mode 100644 index 00000000000..ff5a9d34072 --- /dev/null +++ b/src/osd/modules/input/assignmenthelper.h @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +//============================================================ +// +// assignmenthelper.h - input assignment setup helper +// +//============================================================ +#ifndef MAME_OSD_INPUT_ASSIGNMENTHELPER_H +#define MAME_OSD_INPUT_ASSIGNMENTHELPER_H + +#pragma once + +#include "interface/inputcode.h" +#include "interface/inputdev.h" + +#include <initializer_list> + + +namespace osd { + +class joystick_assignment_helper +{ +protected: + static constexpr input_code make_code( + input_item_class itemclass, + input_item_modifier modifier, + input_item_id item) + { + return input_code(DEVICE_CLASS_JOYSTICK, 0, itemclass, modifier, item); + } + + static bool add_assignment( + input_device::assignment_vector &assignments, + ioport_type fieldtype, + input_seq_type seqtype, + input_item_class itemclass, + input_item_modifier modifier, + std::initializer_list<input_item_id> items); + + static bool add_button_assignment( + input_device::assignment_vector &assignments, + ioport_type field_type, + std::initializer_list<input_item_id> items); + + static bool add_button_pair_assignment( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id button1, + input_item_id button2); + + static bool add_axis_inc_dec_assignment( + input_device::assignment_vector &assignments, + ioport_type field_type, + input_item_id button_dec, + input_item_id button_inc); + + static bool add_axis_pair_assignment( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id axis); + + static bool consume_button_pair( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id &button1, + input_item_id &button2); + + static bool consume_trigger_pair( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id &axis1, + input_item_id &axis2); + + static bool consume_axis_pair( + input_device::assignment_vector &assignments, + ioport_type field1, + ioport_type field2, + input_item_id &axis); + + static void add_directional_assignments( + input_device::assignment_vector &assignments, + input_item_id xaxis, + input_item_id yaxis, + input_item_id leftswitch, + input_item_id rightswitch, + input_item_id upswitch, + input_item_id downswitch); + + static void add_twin_stick_assignments( + input_device::assignment_vector &assignments, + input_item_id leftx, + input_item_id lefty, + input_item_id rightx, + input_item_id righty, + input_item_id leftleft, + input_item_id leftright, + input_item_id leftup, + input_item_id leftdown, + input_item_id rightleft, + input_item_id rightright, + input_item_id rightup, + input_item_id rightdown); + + static void choose_primary_stick( + input_item_id (&stickaxes)[2][2], + input_item_id leftx, + input_item_id lefty, + input_item_id rightx, + input_item_id righty); +}; + +} // namespace osd + +#endif // MAME_OSD_INPUT_ASSIGNMENTHELPER_H diff --git a/src/osd/modules/input/input_common.cpp b/src/osd/modules/input/input_common.cpp index 7127a7878fa..f3de3ca186e 100644 --- a/src/osd/modules/input/input_common.cpp +++ b/src/osd/modules/input/input_common.cpp @@ -8,180 +8,182 @@ // //============================================================ -#include "modules/lib/osdobj_common.h" - -// MAME headers -#include "emu.h" #include "input_common.h" -#include "inputdev.h" // FIXME: still using concrete input device class in input_module_base::poll +#include "modules/lib/osdobj_common.h" -// winnt.h defines this -#ifdef DELETE -#undef DELETE -#endif +#include "emu.h" // so we can get an input manager from the running machine //============================================================ // Keyboard translation table //============================================================ -#if defined(OSD_WINDOWS) +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include <windows.h> -#define KEY_TRANS_ENTRY0(mame, sdlsc, sdlkey, disc, virtual, ascii, UI) { ITEM_ID_##mame, KEY_ ## disc, virtual, ascii, "ITEM_ID_"#mame, (char *) UI } -#define KEY_TRANS_ENTRY1(mame, sdlsc, sdlkey, disc, virtual, ascii) { ITEM_ID_##mame, KEY_ ## disc, virtual, ascii, "ITEM_ID_"#mame, (char*) #mame } -#elif defined(OSD_SDL) -// SDL include +#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 + +#if defined(OSD_SDL) || defined(SDLMAME_WIN32) #include <SDL2/SDL.h> -#define KEY_TRANS_ENTRY0(mame, sdlsc, sdlkey, disc, virtual, ascii, UI) { ITEM_ID_##mame, SDL_SCANCODE_ ## sdlsc, ascii, "ITEM_ID_"#mame, (char *) UI } -#define KEY_TRANS_ENTRY1(mame, sdlsc, sdlkey, disc, virtual, ascii) { ITEM_ID_##mame, SDL_SCANCODE_ ## sdlsc, ascii, "ITEM_ID_"#mame, (char*) #mame } +#define KEY_TRANS_SDL(sdlsc) SDL_SCANCODE_##sdlsc, #else -// osd mini +#define KEY_TRANS_SDL(sdlsc) #endif -// FIXME: sdl_key can be removed from the table below. It is no longer used. +#define KEY_TRANS_ENTRY0(mame, sdlsc, disc, virtual, ascii, UI) { ITEM_ID_##mame, KEY_TRANS_SDL(sdlsc) KEY_TRANS_WIN32(disc, virtual) ascii, "ITEM_ID_"#mame, UI } +#define KEY_TRANS_ENTRY1(mame, sdlsc, disc, virtual, ascii) { ITEM_ID_##mame, KEY_TRANS_SDL(sdlsc) KEY_TRANS_WIN32(disc, virtual) ascii, "ITEM_ID_"#mame, #mame } + +// winnt.h defines this +#ifdef DELETE +#undef DELETE +#endif #if defined(OSD_WINDOWS) || defined(OSD_SDL) + key_trans_entry keyboard_trans_table::s_default_table[] = { - // MAME key sdl scancode sdl key di scancode virtual key ascii ui - KEY_TRANS_ENTRY0(ESC, ESCAPE, ESCAPE, ESCAPE, VK_ESCAPE, 27, "ESCAPE"), - KEY_TRANS_ENTRY1(1, 1, 1, 1, '1', '1'), - KEY_TRANS_ENTRY1(2, 2, 2, 2, '2', '2'), - KEY_TRANS_ENTRY1(3, 3, 3, 3, '3', '3'), - KEY_TRANS_ENTRY1(4, 4, 4, 4, '4', '4'), - KEY_TRANS_ENTRY1(5, 5, 5, 5, '5', '5'), - KEY_TRANS_ENTRY1(6, 6, 6, 6, '6', '6'), - KEY_TRANS_ENTRY1(7, 7, 7, 7, '7', '7'), - KEY_TRANS_ENTRY1(8, 8, 8, 8, '8', '8'), - KEY_TRANS_ENTRY1(9, 9, 9, 9, '9', '9'), - KEY_TRANS_ENTRY1(0, 0, 0, 0, '0', '0'), - KEY_TRANS_ENTRY1(MINUS, MINUS, MINUS, MINUS, VK_OEM_MINUS, '-'), - KEY_TRANS_ENTRY1(EQUALS, EQUALS, EQUALS, EQUALS, VK_OEM_PLUS, '='), - KEY_TRANS_ENTRY1(BACKSPACE, BACKSPACE, BACKSPACE, BACK, VK_BACK, 8), - KEY_TRANS_ENTRY1(TAB, TAB, TAB, TAB, VK_TAB, 9), - KEY_TRANS_ENTRY1(Q, Q, q, Q, 'Q', 'Q'), - KEY_TRANS_ENTRY1(W, W, w, W, 'W', 'W'), - KEY_TRANS_ENTRY1(E, E, e, E, 'E', 'E'), - KEY_TRANS_ENTRY1(R, R, r, R, 'R', 'R'), - KEY_TRANS_ENTRY1(T, T, t, T, 'T', 'T'), - KEY_TRANS_ENTRY1(Y, Y, y, Y, 'Y', 'Y'), - KEY_TRANS_ENTRY1(U, U, u, U, 'U', 'U'), - KEY_TRANS_ENTRY1(I, I, i, I, 'I', 'I'), - KEY_TRANS_ENTRY1(O, O, o, O, 'O', 'O'), - KEY_TRANS_ENTRY1(P, P, p, P, 'P', 'P'), - KEY_TRANS_ENTRY1(OPENBRACE, LEFTBRACKET, LEFTBRACKET, LBRACKET, VK_OEM_4, '['), - KEY_TRANS_ENTRY1(CLOSEBRACE, RIGHTBRACKET, RIGHTBRACKET, RBRACKET, VK_OEM_6, ']'), - KEY_TRANS_ENTRY0(ENTER, RETURN, RETURN, RETURN, VK_RETURN, 13, "RETURN"), - KEY_TRANS_ENTRY1(LCONTROL, LCTRL, LCTRL, LCONTROL, VK_LCONTROL, 0), - KEY_TRANS_ENTRY1(A, A, a, A, 'A', 'A'), - KEY_TRANS_ENTRY1(S, S, s, S, 'S', 'S'), - KEY_TRANS_ENTRY1(D, D, d, D, 'D', 'D'), - KEY_TRANS_ENTRY1(F, F, f, F, 'F', 'F'), - KEY_TRANS_ENTRY1(G, G, g, G, 'G', 'G'), - KEY_TRANS_ENTRY1(H, H, h, H, 'H', 'H'), - KEY_TRANS_ENTRY1(J, J, j, J, 'J', 'J'), - KEY_TRANS_ENTRY1(K, K, k, K, 'K', 'K'), - KEY_TRANS_ENTRY1(L, L, l, L, 'L', 'L'), - KEY_TRANS_ENTRY1(COLON, SEMICOLON, SEMICOLON, SEMICOLON, VK_OEM_1, ';'), - KEY_TRANS_ENTRY1(QUOTE, APOSTROPHE, QUOTE, APOSTROPHE, VK_OEM_7, '\''), - KEY_TRANS_ENTRY1(TILDE, GRAVE, BACKQUOTE, GRAVE, VK_OEM_3, '`'), - KEY_TRANS_ENTRY1(LSHIFT, LSHIFT, LSHIFT, LSHIFT, VK_LSHIFT, 0), - KEY_TRANS_ENTRY1(BACKSLASH, BACKSLASH, BACKSLASH, BACKSLASH, VK_OEM_5, '\\'), -// KEY_TRANS_ENTRY1(BACKSLASH2, NONUSHASH, UNKNOWN, OEM_102, VK_OEM_102, '<'), + // 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, UNKNOWN, OEM_102, VK_OEM_102, '<'), - KEY_TRANS_ENTRY1(Z, Z, z, Z, 'Z', 'Z'), - KEY_TRANS_ENTRY1(X, X, x, X, 'X', 'X'), - KEY_TRANS_ENTRY1(C, C, c, C, 'C', 'C'), - KEY_TRANS_ENTRY1(V, V, v, V, 'V', 'V'), - KEY_TRANS_ENTRY1(B, B, b, B, 'B', 'B'), - KEY_TRANS_ENTRY1(N, N, n, N, 'N', 'N'), - KEY_TRANS_ENTRY1(M, M, m, M, 'M', 'M'), - KEY_TRANS_ENTRY1(COMMA, COMMA, COMMA, COMMA, VK_OEM_COMMA, ','), - KEY_TRANS_ENTRY1(STOP, PERIOD, PERIOD, PERIOD, VK_OEM_PERIOD, '.'), - KEY_TRANS_ENTRY1(SLASH, SLASH, SLASH, SLASH, VK_OEM_2, '/'), - KEY_TRANS_ENTRY1(RSHIFT, RSHIFT, RSHIFT, RSHIFT, VK_RSHIFT, 0), - KEY_TRANS_ENTRY1(ASTERISK, KP_MULTIPLY, KP_MULTIPLY, MULTIPLY, VK_MULTIPLY, '*'), - KEY_TRANS_ENTRY1(LALT, LALT, LALT, LMENU, VK_LMENU, 0), - KEY_TRANS_ENTRY1(SPACE, SPACE, SPACE, SPACE, VK_SPACE, ' '), - KEY_TRANS_ENTRY1(CAPSLOCK, CAPSLOCK, CAPSLOCK, CAPITAL, VK_CAPITAL, 0), - KEY_TRANS_ENTRY1(F1, F1, F1, F1, VK_F1, 0), - KEY_TRANS_ENTRY1(F2, F2, F2, F2, VK_F2, 0), - KEY_TRANS_ENTRY1(F3, F3, F3, F3, VK_F3, 0), - KEY_TRANS_ENTRY1(F4, F4, F4, F4, VK_F4, 0), - KEY_TRANS_ENTRY1(F5, F5, F5, F5, VK_F5, 0), - KEY_TRANS_ENTRY1(F6, F6, F6, F6, VK_F6, 0), - KEY_TRANS_ENTRY1(F7, F7, F7, F7, VK_F7, 0), - KEY_TRANS_ENTRY1(F8, F8, F8, F8, VK_F8, 0), - KEY_TRANS_ENTRY1(F9, F9, F9, F9, VK_F9, 0), - KEY_TRANS_ENTRY1(F10, F10, F10, F10, VK_F10, 0), - KEY_TRANS_ENTRY1(NUMLOCK, NUMLOCKCLEAR, NUMLOCKCLEAR, NUMLOCK, VK_NUMLOCK, 0), - KEY_TRANS_ENTRY1(SCRLOCK, SCROLLLOCK, SCROLLLOCK, SCROLL, VK_SCROLL, 0), - KEY_TRANS_ENTRY1(7_PAD, KP_7, KP_7, NUMPAD7, VK_NUMPAD7, 0), - KEY_TRANS_ENTRY1(8_PAD, KP_8, KP_8, NUMPAD8, VK_NUMPAD8, 0), - KEY_TRANS_ENTRY1(9_PAD, KP_9, KP_9, NUMPAD9, VK_NUMPAD9, 0), - KEY_TRANS_ENTRY1(MINUS_PAD, KP_MINUS, KP_MINUS, SUBTRACT, VK_SUBTRACT, 0), - KEY_TRANS_ENTRY1(4_PAD, KP_4, KP_4, NUMPAD4, VK_NUMPAD4, 0), - KEY_TRANS_ENTRY1(5_PAD, KP_5, KP_5, NUMPAD5, VK_NUMPAD5, 0), - KEY_TRANS_ENTRY1(6_PAD, KP_6, KP_6, NUMPAD6, VK_NUMPAD6, 0), - KEY_TRANS_ENTRY1(PLUS_PAD, KP_PLUS, KP_PLUS, ADD, VK_ADD, 0), - KEY_TRANS_ENTRY1(1_PAD, KP_1, KP_1, NUMPAD1, VK_NUMPAD1, 0), - KEY_TRANS_ENTRY1(2_PAD, KP_2, KP_2, NUMPAD2, VK_NUMPAD2, 0), - KEY_TRANS_ENTRY1(3_PAD, KP_3, KP_3, NUMPAD3, VK_NUMPAD3, 0), - KEY_TRANS_ENTRY1(0_PAD, KP_0, KP_0, NUMPAD0, VK_NUMPAD0, 0), - KEY_TRANS_ENTRY1(DEL_PAD, KP_PERIOD, KP_PERIOD, DECIMAL, VK_DECIMAL, 0), - KEY_TRANS_ENTRY1(F11, F11, F11, F11, VK_F11, 0), - KEY_TRANS_ENTRY1(F12, F12, F12, F12, VK_F12, 0), - KEY_TRANS_ENTRY1(F13, F13, F13, F13, VK_F13, 0), - KEY_TRANS_ENTRY1(F14, F14, F14, F14, VK_F14, 0), - KEY_TRANS_ENTRY1(F15, F15, F15, F15, VK_F15, 0), - KEY_TRANS_ENTRY1(ENTER_PAD, KP_ENTER, KP_ENTER, NUMPADENTER, VK_RETURN, 0), - KEY_TRANS_ENTRY1(RCONTROL, RCTRL, RCTRL, RCONTROL, VK_RCONTROL, 0), - KEY_TRANS_ENTRY1(SLASH_PAD, KP_DIVIDE, KP_DIVIDE, DIVIDE, VK_DIVIDE, 0), - KEY_TRANS_ENTRY1(PRTSCR, PRINTSCREEN, PRINTSCREEN, SYSRQ, 0, 0), - KEY_TRANS_ENTRY1(RALT, RALT, RALT, RMENU, VK_RMENU, 0), - KEY_TRANS_ENTRY1(HOME, HOME, HOME, HOME, VK_HOME, 0), - KEY_TRANS_ENTRY1(UP, UP, UP, UP, VK_UP, 0), - KEY_TRANS_ENTRY1(PGUP, PAGEUP, PAGEUP, PRIOR, VK_PRIOR, 0), - KEY_TRANS_ENTRY1(LEFT, LEFT, LEFT, LEFT, VK_LEFT, 0), - KEY_TRANS_ENTRY1(RIGHT, RIGHT, RIGHT, RIGHT, VK_RIGHT, 0), - KEY_TRANS_ENTRY1(END, END, END, END, VK_END, 0), - KEY_TRANS_ENTRY1(DOWN, DOWN, DOWN, DOWN, VK_DOWN, 0), - KEY_TRANS_ENTRY1(PGDN, PAGEDOWN, PAGEDOWN, NEXT, VK_NEXT, 0), - KEY_TRANS_ENTRY1(INSERT, INSERT, INSERT, INSERT, VK_INSERT, 0), - KEY_TRANS_ENTRY0(DEL, DELETE, DELETE, DELETE, VK_DELETE, 0, "DELETE"), - KEY_TRANS_ENTRY1(LWIN, LGUI, LGUI, LWIN, VK_LWIN, 0), - KEY_TRANS_ENTRY1(RWIN, RGUI, RGUI, RWIN, VK_RWIN, 0), - KEY_TRANS_ENTRY1(MENU, MENU, MENU, APPS, VK_APPS, 0), - KEY_TRANS_ENTRY1(PAUSE, PAUSE, PAUSE, PAUSE, VK_PAUSE, 0), - KEY_TRANS_ENTRY0(CANCEL, CANCEL, CANCEL, UNKNOWN, 0, 0, "CANCEL"), - KEY_TRANS_ENTRY1(BS_PAD, KP_BACKSPACE, KP_BACKSPACE, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(TAB_PAD, KP_TAB, KP_TAB, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(00_PAD, KP_00, KP_00, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(000_PAD, KP_000, KP_000, UNKNOWN, 0, 0), - KEY_TRANS_ENTRY1(COMMA_PAD, KP_COMMA, KP_COMMA, NUMPADCOMMA, 0, 0), - KEY_TRANS_ENTRY1(EQUALS_PAD, KP_EQUALS, 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, MUTE, VK_VOLUME_MUTE, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, VOLUMEDOWN, VOLUMEDOWN, VOLUMEDOWN, VK_VOLUME_DOWN, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, VOLUMEUP, VOLUMEUP, VOLUMEUP, VK_VOLUME_UP, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_HOME, AC_HOME, WEBHOME, VK_BROWSER_HOME, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_SEARCH, AC_SEARCH, WEBSEARCH, VK_BROWSER_SEARCH, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_BOOKMARKS, AC_BOOKMARKS, WEBFAVORITES, VK_BROWSER_FAVORITES, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_REFRESH, AC_REFRESH, WEBREFRESH, VK_BROWSER_REFRESH, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_STOP, AC_STOP, WEBSTOP, VK_BROWSER_STOP, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_FORWARD, AC_FORWARD, WEBFORWARD, VK_BROWSER_FORWARD, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, AC_BACK, AC_BACK, WEBBACK, VK_BROWSER_BACK, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, MAIL, MAIL, MAIL, VK_LAUNCH_MAIL, 0), - KEY_TRANS_ENTRY1(OTHER_SWITCH, MEDIASELECT, MEDIASELECT, MEDIASELECT, VK_LAUNCH_MEDIA_SELECT, 0), - KEY_TRANS_ENTRY0(INVALID, UNKNOWN, 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) { @@ -226,14 +232,12 @@ input_item_id keyboard_trans_table::lookup_mame_code(const char *scode) const } // Windows specific lookup methods -#if defined(OSD_WINDOWS) +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) input_item_id keyboard_trans_table::map_di_scancode_to_itemid(int scancode) const { - int tablenum; - // 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].scan_code == scancode) return m_table[tablenum].mame_key; @@ -250,75 +254,53 @@ 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; } return 0; } -#endif +#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -int input_module_base::init(const osd_options &options) +input_module_base::input_module_base(const char *type, const char* name) : + osd_module(type, name), + m_clock(), + m_last_poll(timepoint_type::min()), + m_background_input(false), + m_options(nullptr), + m_manager(nullptr) { - m_options = &options; - - m_mouse_enabled = options.mouse(); - m_lightgun_enabled = options.lightgun(); - - int result = init_internal(); - if (result != 0) - return result; - - m_input_paused = false; - m_input_enabled = true; - - return 0; } -void input_module_base::poll(running_machine &machine) +int input_module_base::init(osd_interface &osd, const osd_options &options) { - // ignore if not enabled - if (m_input_enabled) - { - // grab the current time - m_last_poll = m_clock.now(); - - before_poll(machine); + m_options = &options; - // track if mouse/lightgun is enabled, for mouse hiding purposes - m_mouse_enabled = machine.input().device_class(DEVICE_CLASS_MOUSE).enabled(); - m_lightgun_enabled = machine.input().device_class(DEVICE_CLASS_LIGHTGUN).enabled(); - } + // don't enable background input when debugging + m_background_input = !options.debug() && options.background_input(); - // poll all of the devices - if (should_poll_devices(machine)) - { - m_devicelist.poll_devices(); - } - else - { - m_devicelist.reset_devices(); - } + return 0; } -void input_module_base::pause() +void input_module_base::input_init(running_machine &machine) { - // keep track of the paused state - m_input_paused = true; + m_manager = &machine.input(); } -void input_module_base::resume() +void input_module_base::poll_if_necessary(bool relative_reset) { - // keep track of the paused state - m_input_paused = false; -} + timepoint_type const now = m_clock.now(); + if (relative_reset || (now >= (m_last_poll + std::chrono::milliseconds(MIN_POLLING_INTERVAL)))) + { + // grab the current time + m_last_poll = now; -void input_module_base::exit() -{ - devicelist().free_all_devices(); + before_poll(); + + poll(relative_reset); + } } diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h index e220445c266..28e53fb0ccb 100644 --- a/src/osd/modules/input/input_common.h +++ b/src/osd/modules/input/input_common.h @@ -14,9 +14,14 @@ #include "input_module.h" +#include "interface/inputdev.h" #include "interface/inputman.h" +#include "modules/osdmodule.h" + +#include "util/strformat.h" #include <algorithm> +#include <cassert> #include <chrono> #include <functional> #include <memory> @@ -42,225 +47,66 @@ 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 //============================================================ -class input_device_list; - class device_info { - friend input_device_list; - private: const std::string m_name; const std::string m_id; - osd::input_device * m_device; - running_machine & m_machine; input_module & m_module; - input_device_class m_deviceclass; public: // Constructor - device_info(running_machine &machine, std::string &&name, std::string &&id, input_device_class deviceclass, input_module &module) : + device_info(std::string &&name, std::string &&id, input_module &module) : m_name(std::move(name)), m_id(std::move(id)), - m_device(nullptr), - m_machine(machine), - m_module(module), - m_deviceclass(deviceclass) + m_module(module) { } // Destructor - virtual ~device_info() { } + virtual ~device_info() = default; // Getters - running_machine & machine() const { return m_machine; } - const std::string & name() const { return m_name; } - const std::string & id() const { return m_id; } - osd::input_device * device() const { return m_device; } - input_module & module() const { return m_module; } - input_device_class deviceclass() const { return m_deviceclass; } + const std::string &name() const { return m_name; } + const std::string &id() const { return m_id; } + input_module &module() const { return m_module; } // Poll and reset methods - virtual void poll() { } + virtual void poll(bool relative_reset) = 0; virtual void reset() = 0; + virtual void configure(osd::input_device &device) = 0; }; + //============================================================ // event_based_device //============================================================ -#define DEFAULT_EVENT_QUEUE_SIZE 20 - template <class TEvent> class event_based_device : public device_info { private: - std::queue<TEvent> m_event_queue; + static inline constexpr unsigned DEFAULT_EVENT_QUEUE_SIZE = 20; + + std::queue<TEvent> m_event_queue; protected: std::mutex m_device_lock; - virtual void process_event(TEvent &ev) = 0; + virtual void process_event(TEvent const &ev) = 0; public: - event_based_device(running_machine &machine, std::string &&name, std::string &&id, input_device_class deviceclass, input_module &module) : - device_info(machine, std::move(name), std::move(id), deviceclass, module) + event_based_device(std::string &&name, std::string &&id, input_module &module) : + device_info(std::move(name), std::move(id), module) { } - void queue_events(const TEvent *events, int count) + void queue_events(TEvent const *events, int count) { std::lock_guard<std::mutex> scope_lock(m_device_lock); for (int i = 0; i < count; i++) @@ -271,39 +117,40 @@ public: m_event_queue.pop(); } - void virtual poll() override + void virtual poll(bool relative_reset) override { std::lock_guard<std::mutex> scope_lock(m_device_lock); // Process each event until the queue is empty while (!m_event_queue.empty()) { - TEvent &next_event = m_event_queue.front(); - process_event(next_event); + process_event(m_event_queue.front()); m_event_queue.pop(); } } }; + //============================================================ // input_device_list class //============================================================ +template <typename Info> class input_device_list { private: - std::vector<std::unique_ptr<device_info>> m_list; + std::vector<std::unique_ptr<Info> > m_list; public: auto size() const { return m_list.size(); } auto empty() const { return m_list.empty(); } - auto begin() { return m_list.begin(); } - auto end() { return m_list.end(); } + auto begin() const { return m_list.begin(); } + auto end() const { return m_list.end(); } - void poll_devices() + void poll_devices(bool relative_reset) { for (auto &device: m_list) - device->poll(); + device->poll(relative_reset); } void reset_devices() @@ -312,18 +159,11 @@ public: device->reset(); } - void free_device(device_info &devinfo) - { - // find the device to remove - const auto device_matches = [&devinfo] (std::unique_ptr<device_info> &device) { return &devinfo == device.get(); }; - m_list.erase(std::remove_if(std::begin(m_list), std::end(m_list), device_matches), m_list.end()); - } - template <typename T> void for_each_device(T &&action) { for (auto &device: m_list) - action(device.get()); + action(*device); } void free_all_devices() @@ -332,41 +172,34 @@ public: m_list.pop_back(); } - template <typename TActual, typename... TArgs> - TActual &create_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module, TArgs &&... args) + template <typename Actual> + Actual &add_device(std::unique_ptr<Actual> &&devinfo) { - // allocate the device object - auto devinfo = std::make_unique<TActual>(machine, std::move(name), std::move(id), module, std::forward<TArgs>(args)...); - - return add_device(machine.input(), std::move(devinfo)); - } - - template <typename TActual> - TActual &add_device(osd::input_manager &manager, std::unique_ptr<TActual> &&devinfo) - { - // Add the device to the machine - devinfo->m_device = &manager.add_device(devinfo->deviceclass(), devinfo->name(), devinfo->id(), devinfo.get()); - - // append us to the list - return *static_cast<TActual *>(m_list.emplace_back(std::move(devinfo)).get()); + // append us to the list and return reference + Actual &result = *devinfo; + m_list.emplace_back(std::move(devinfo)); + return result; } }; + // keyboard translation table -struct key_trans_entry { +struct key_trans_entry +{ input_item_id mame_key; -#if defined(OSD_SDL) +#if defined(OSD_SDL) || defined(SDLMAME_WIN32) int sdl_scancode; -#elif defined(OSD_WINDOWS) - int scan_code; +#endif +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) + uint16_t scan_code; unsigned char virtual_key; #endif char ascii_key; - char const * mame_key_name; - char * ui_name; + char const * mame_key_name; + char const * ui_name; }; class keyboard_trans_table @@ -379,7 +212,7 @@ private: std::unique_ptr<key_trans_entry[]> m_custom_table; key_trans_entry * m_table; - uint32_t m_table_size; + uint32_t m_table_size; public: // constructor @@ -392,7 +225,7 @@ public: input_item_id lookup_mame_code(const char * scode) const; int lookup_mame_index(const char * scode) const; -#if defined(OSD_WINDOWS) +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) input_item_id map_di_scancode_to_itemid(int di_scancode) const; int vkey_for_mame_code(input_code code) const; #endif @@ -406,97 +239,137 @@ public: key_trans_entry & operator [](int i) const { return m_table[i]; } }; + //============================================================ // input_module_base - base class for input modules //============================================================ -class osd_options; +class input_module_base : public osd_module, public input_module +{ +private: + // 10 milliseconds polling interval + static constexpr inline unsigned MIN_POLLING_INTERVAL = 2; -typedef std::chrono::high_resolution_clock clock_type; -typedef std::chrono::time_point<std::chrono::high_resolution_clock> timepoint_type; + using clock_type = std::chrono::high_resolution_clock; + using timepoint_type = std::chrono::time_point<std::chrono::high_resolution_clock>; -// 10 milliseconds polling interval -#define MIN_POLLING_INTERVAL 10 + clock_type m_clock; + timepoint_type m_last_poll; + bool m_background_input; + const osd_options * m_options; + osd::input_manager * m_manager; + + virtual void poll(bool relative_reset) = 0; + +protected: + input_module_base(char const *type, char const *name); + + osd::input_manager & manager() { assert(m_manager); return *m_manager; } + const osd_options * options() const { return m_options; } + bool background_input() const { return m_background_input; } + + virtual void before_poll() { } + +public: + virtual int init(osd_interface &osd, const osd_options &options) override; + + virtual void input_init(running_machine &machine) override; + virtual void poll_if_necessary(bool relative_reset) override; + + virtual void reset_devices() = 0; // SDL OSD uses this to forcibly release keys +}; -class input_module_base : public input_module + +//============================================================ +// input_module_impl - base class for input modules +//============================================================ + +template <typename Info, typename OsdImpl> +class input_module_impl : public input_module_base { public: - input_module_base(const char *type, const char* name) : - input_module(type, name), - m_input_enabled(false), - m_mouse_enabled(false), - m_lightgun_enabled(false), - m_input_paused(false), - m_options(nullptr) + virtual void exit() override { + devicelist().free_all_devices(); } -private: - bool m_input_enabled; - bool m_mouse_enabled; - bool m_lightgun_enabled; - bool m_input_paused; - const osd_options * m_options; - input_device_list m_devicelist; - clock_type m_clock; - timepoint_type m_last_poll; + virtual int init(osd_interface &osd, const osd_options &options) override + { + m_osd = dynamic_cast<OsdImpl *>(&osd); + if (!m_osd) + return -1; + + return input_module_base::init(osd, options); + } + + virtual void reset_devices() override { devicelist().reset_devices(); } protected: - void set_mouse_enabled(bool value) { m_mouse_enabled = value; } + using input_module_base::input_module_base; -public: + input_device_list<Info> &devicelist() { return m_devicelist; } + OsdImpl &osd() { assert(m_osd); return *m_osd; } - const osd_options * options() const { return m_options; } - input_device_list & devicelist() { return m_devicelist; } - bool input_enabled() const { return m_input_enabled; } - bool input_paused() const { return m_input_paused; } - bool mouse_enabled() const { return m_mouse_enabled; } - bool lightgun_enabled() const { return m_lightgun_enabled; } + virtual void before_poll() override + { + // periodically process events, in case they're not coming through + // this also will make sure the mouse state is up-to-date + osd().process_events(); + } + + virtual bool should_poll_devices() + { + return background_input() || osd().has_focus(); + } - virtual int init(const osd_options &options) override; + template <typename Actual, typename... Params> + Actual &create_device(input_device_class deviceclass, std::string &&name, std::string &&id, Params &&... args) + { + // allocate the device object and add it to the input manager + return add_device( + deviceclass, + std::make_unique<Actual>(std::move(name), std::move(id), *this, std::forward<Params>(args)...)); + } - virtual void poll_if_necessary(running_machine &machine) override + template <typename Actual> + Actual &add_device(input_device_class deviceclass, std::unique_ptr<Actual> &&devinfo) { - auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(m_clock.now() - m_last_poll); - if (elapsed.count() >= MIN_POLLING_INTERVAL) - { - poll(machine); - } + // add it to the input manager and append it to the list + osd::input_device &osddev = manager().add_device(deviceclass, devinfo->name(), devinfo->id(), devinfo.get()); + devinfo->configure(osddev); + return devicelist().add_device(std::move(devinfo)); } - virtual void pause() override; - virtual void resume() override; - virtual void exit() override; +private: + virtual void poll(bool relative_reset) override final + { + // poll all of the devices + if (should_poll_devices()) + m_devicelist.poll_devices(relative_reset); + else + m_devicelist.reset_devices(); + } -protected: - virtual int init_internal() { return 0; } - virtual void poll(running_machine &machine); - virtual bool should_poll_devices(running_machine &machine) = 0; - virtual void before_poll(running_machine &machine) { } + input_device_list<Info> m_devicelist; + OsdImpl *m_osd = nullptr; }; + template <class TItem> int generic_button_get_state(void *device_internal, void *item_internal) { - device_info *devinfo = static_cast<device_info *>(device_internal); - TItem *itemdata = static_cast<TItem*>(item_internal); - // return the current state - devinfo->module().poll_if_necessary(devinfo->machine()); - return *itemdata >> 7; + return *reinterpret_cast<TItem const *>(item_internal) >> 7; } + template <class TItem> int generic_axis_get_state(void *device_internal, void *item_internal) { - device_info *devinfo = static_cast<device_info *>(device_internal); - TItem *axisdata = static_cast<TItem*>(item_internal); - - // return the current state - devinfo->module().poll_if_necessary(devinfo->machine()); - return *axisdata; + return *reinterpret_cast<TItem const *>(item_internal); } + //============================================================ // default_button_name //============================================================ @@ -524,23 +397,22 @@ const char *const default_axis_name[] = inline int32_t normalize_absolute_axis(double raw, double rawmin, double rawmax) { - double center = (rawmax + rawmin) / 2.0; - - // make sure we have valid data + // make sure we have valid arguments if (rawmin >= rawmax) return int32_t(raw); + double const center = (rawmax + rawmin) / 2.0; if (raw >= center) { // above center - double result = (raw - center) * osd::INPUT_ABSOLUTE_MAX / (rawmax - center); - return std::min(result, (double)osd::INPUT_ABSOLUTE_MAX); + double const result = (raw - center) * double(osd::input_device::ABSOLUTE_MAX) / (rawmax - center); + return int32_t(std::min(result, double(osd::input_device::ABSOLUTE_MAX))); } else { // below center - double result = -((center - raw) * (double)-osd::INPUT_ABSOLUTE_MIN / (center - rawmin)); - return std::max(result, (double)osd::INPUT_ABSOLUTE_MIN); + double result = -((center - raw) * double(-osd::input_device::ABSOLUTE_MIN) / (center - rawmin)); + return int32_t(std::max(result, double(osd::input_device::ABSOLUTE_MIN))); } } diff --git a/src/osd/modules/input/input_dinput.cpp b/src/osd/modules/input/input_dinput.cpp index 77467f80d59..7732d610bba 100644 --- a/src/osd/modules/input/input_dinput.cpp +++ b/src/osd/modules/input/input_dinput.cpp @@ -1,155 +1,354 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles, Brad Hughes +// copyright-holders:Aaron Giles, Brad Hughes, Vas Crabb //============================================================ // // input_dinput.cpp - Windows DirectInput support // //============================================================ +/* + +DirectInput joystick input is a bit of a mess. It gives eight axes +called X, Y, Z, Rx, Ry, Rz, slider 0 and slider 1. The driver can +assign arbitrary physical axes to these axes. Up to four hat switches +are supported, giving a direction in hundredths of degrees. In theory, +this supports dial-like controls with an arbitrary number of stops. In +practice, it just makes dealing with 8-way hat switches more complicated +and prevents contradictory inputs from being reported altogether. + +You may get a vague indication of the type of controller, and you can +obtain usage information for HID controllers. + +The Windows HID driver supposedly uses the following mappings: + +0x01 Generic Desktop 0x30 X X +0x01 Generic Desktop 0x31 Y Y +0x01 Generic Desktop 0x32 Z Z +0x01 Generic Desktop 0x33 Rx Rx +0x01 Generic Desktop 0x34 Ry Ry +0x01 Generic Desktop 0x35 Rz Rz +0x01 Generic Desktop 0x36 Slider Slider +0x01 Generic Desktop 0x37 Dial Slider +0x01 Generic Desktop 0x39 Hat Switch POV Hat +0x02 Simulation 0xBA Rudder Rz +0x02 Simulation 0xBB Throttle Slider +0x02 Simulation 0xC4 Accelerator Y +0x02 Simulation 0xC5 Brake Rz +0x02 Simulation 0xC8 Steering X + +Anything without an explicit mapping is treated as a button. + +The WinMM driver supposedly uses the following axis mappings: + +X X +Y Y +Z Slider +R Rz +U Slider +V Slider + +The actual mapping used by various controllers doesn't match what you +might expect from the HID mapping. + +Gamepads: + +Axis Logitech DS4 Xinput Switch +X Left X Left X Left X Left X +Y Left Y Left Y Left Y Left Y +Z Right X Right X Triggers +Rx Left trigger Right X Right X +Ry Right trigger Right Y Right Y +Rz Right Y Right Y + +Thrustmaster controllers: + +Axis HOTAS Side stick Throttle/Pedals Dual Throttles Triple Throttles Driving +X Aileron Aileron Mini Stick X Left Throttle Right Brake Steering +Y Elevator Elevator Mini stick Y Right Throttle Left Brake Brake +Z Throttle Throttle Flaps Rudder +Rx Right Brake Right Brake Right Brake Left Throttle +Ry Left Brake Left Brake Left Brake Centre Throttle +Rz Twist Rudder Rocker Air Brake Right Throttle Accelerator +Slider 0 Rocker Throttle Antenna Rudder Clutch +Slider 1 Rudder Rudder + +Logitech controllers: + +Axis Pro Wheels +X Steering +Y +Z +Rx Accelerator +Ry Brake +Rz Clutch +Slider 0 +Slider 1 + +MFG Crosswind pedals: + +X Left Brake +Y Right Brake +Rz Rudder + +*/ #include "modules/osdmodule.h" -#if defined(OSD_WINDOWS) - -#include "emu.h" +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include "input_dinput.h" -#include "winutil.h" +#include "interface/inputseq.h" +#include "windows/winutil.h" + +// emu +#include "inpttype.h" + +// lib/util +#include "util/corestr.h" + +#ifdef SDLMAME_WIN32 +#include <SDL2/SDL.h> +#include <SDL2/SDL_syswm.h> +#endif -#include <mutex> +#include <algorithm> +#include <cmath> +#include <iterator> +#include <memory> // standard windows headers #include <initguid.h> #include <tchar.h> +namespace osd { + namespace { -using namespace Microsoft::WRL; +std::string guid_to_string(GUID const &guid) +{ + // size of a GUID string with dashes plus NUL terminator + char guid_string[37]; + snprintf( + guid_string, std::size(guid_string), + "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid.Data1, guid.Data2, guid.Data3, + guid.Data4[0], guid.Data4[1], guid.Data4[2], + guid.Data4[3], guid.Data4[4], guid.Data4[5], + guid.Data4[6], guid.Data4[7]); + + return guid_string; +} + + //============================================================ -// dinput_joystick_pov_get_state +// dinput_keyboard_device - DirectInput keyboard device //============================================================ -int32_t dinput_joystick_pov_get_state(void *device_internal, void *item_internal) +class dinput_keyboard_device : public dinput_device { - auto *devinfo = static_cast<dinput_joystick_device *>(device_internal); - int povnum = reinterpret_cast<uintptr_t>(item_internal) / 4; - int povdir = reinterpret_cast<uintptr_t>(item_internal) % 4; - int32_t result = 0; +public: + dinput_keyboard_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format); + + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + +private: + std::mutex m_device_lock; + keyboard_state m_keyboard; +}; - // get the current state - devinfo->module().poll_if_necessary(devinfo->machine()); - const DWORD pov = devinfo->joystick.state.rgdwPOV[povnum]; +dinput_keyboard_device::dinput_keyboard_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format) : + dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format), + m_keyboard({ { 0 } }) +{ +} - // if invalid, return 0 - if ((pov & 0xffff) == 0xffff) - return result; +void dinput_keyboard_device::poll(bool relative_reset) +{ + // poll the DirectInput immediate state + std::lock_guard<std::mutex> scope_lock(m_device_lock); + poll_dinput(&m_keyboard.state); +} - // return the current state - switch (povdir) +void dinput_keyboard_device::reset() +{ + memset(&m_keyboard.state, 0, sizeof(m_keyboard.state)); +} + +void dinput_keyboard_device::configure(input_device &device) +{ + // populate it + char defname[20]; + for (unsigned keynum = 0; keynum < MAX_KEYS; keynum++) { - case POVDIR_LEFT: result = (pov >= 22500 && pov <= 31500); break; - case POVDIR_RIGHT: result = (pov >= 4500 && pov <= 13500); break; - case POVDIR_UP: result = (pov >= 31500 || pov <= 4500); break; - case POVDIR_DOWN: result = (pov >= 13500 && pov <= 22500); break; + input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum); + + // generate/fetch the name + snprintf(defname, std::size(defname), "Scan%03d", keynum); + + // add the item to the device + device.add_item( + item_name(keynum, defname, nullptr), + strmakeupper(defname), + itemid, + generic_button_get_state<uint8_t>, + &m_keyboard.state[keynum]); } - return result; } //============================================================ -// dinput_set_dword_property +// dinput_mouse_device - DirectInput mouse device //============================================================ -HRESULT dinput_set_dword_property(ComPtr<IDirectInputDevice8> device, REFGUID property_guid, DWORD object, DWORD how, DWORD value) +class dinput_mouse_device : public dinput_device +{ +public: + dinput_mouse_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format); + + void poll(bool relative_reset) override; + void reset() override; + virtual void configure(input_device &device) override; + +private: + mouse_state m_mouse; +}; + +dinput_mouse_device::dinput_mouse_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format) : + dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format), + m_mouse({0}) { - DIPROPDWORD dipdw; + // cap the number of axes and buttons based on the format + m_caps.dwAxes = std::min(m_caps.dwAxes, DWORD(3)); + m_caps.dwButtons = std::min(m_caps.dwButtons, DWORD((m_format == &c_dfDIMouse) ? 4 : 8)); +} - dipdw.diph.dwSize = sizeof(dipdw); - dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); - dipdw.diph.dwObj = object; - dipdw.diph.dwHow = how; - dipdw.dwData = value; +void dinput_mouse_device::poll(bool relative_reset) +{ + // poll + if (relative_reset && (poll_dinput(&m_mouse) == DI_OK)) + { + // scale the axis data + m_mouse.lX *= input_device::RELATIVE_PER_PIXEL; + m_mouse.lY *= input_device::RELATIVE_PER_PIXEL; + m_mouse.lZ *= input_device::RELATIVE_PER_PIXEL; + } +} - return device->SetProperty(property_guid, &dipdw.diph); +void dinput_mouse_device::reset() +{ + memset(&m_mouse, 0, sizeof(m_mouse)); +} + +void dinput_mouse_device::configure(input_device &device) +{ + // populate the axes + for (int axisnum = 0; axisnum < m_caps.dwAxes; axisnum++) + { + // add to the mouse device and optionally to the gun device as well + device.add_item( + item_name(offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr), + std::string_view(), + input_item_id(ITEM_ID_XAXIS + axisnum), + generic_axis_get_state<LONG>, + &m_mouse.lX + axisnum); + } + + // populate the buttons + for (int butnum = 0; butnum < m_caps.dwButtons; butnum++) + { + auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIMOUSESTATE *>(nullptr)->rgbButtons[butnum]); + + // add to the mouse device + device.add_item( + item_name(offset, default_button_name(butnum), nullptr), + std::string_view(), + input_item_id(ITEM_ID_BUTTON1 + butnum), + generic_button_get_state<BYTE>, + &m_mouse.rgbButtons[butnum]); + } } //============================================================ -// dinput_module - base directinput module +// dinput_module - base DirectInput module //============================================================ -class dinput_module : public wininput_module, public device_enum_interface +class dinput_module : public input_module_impl<dinput_device, osd_common_t> { -protected: - std::unique_ptr<dinput_api_helper> m_dinput_helper; - public: - dinput_module(const char* type, const char* name) - : wininput_module(type, name), - m_dinput_helper(nullptr) + dinput_module(const char* type, const char* name) : + input_module_impl<dinput_device, osd_common_t>(type, name), + m_dinput_helper(nullptr) { } - int init_internal() override + virtual int init(osd_interface &osd, osd_options const &options) override { m_dinput_helper = std::make_unique<dinput_api_helper>(); - int result = m_dinput_helper->initialize(); - if (result != 0) + int const result = m_dinput_helper->initialize(); + if (result) + { + m_dinput_helper.reset(); return result; + } - return 0; + return input_module_impl<dinput_device, osd_common_t>::init(osd, options); } - void exit() override + virtual void exit() override { - wininput_module::exit(); - m_dinput_helper.reset(); - } + input_module_impl<dinput_device, osd_common_t>::exit(); - void input_init(running_machine &machine) override - { - HRESULT result = m_dinput_helper->enum_attached_devices(dinput_devclass(), this, &machine); - if (result != DI_OK) - fatalerror("DirectInput: Unable to enumerate devices (result=%08X)\n", uint32_t(result)); + m_dinput_helper.reset(); } - static std::string device_item_name(dinput_device *devinfo, int offset, const char *defstring, const char *suffix) + virtual void input_init(running_machine &machine) override { - DIDEVICEOBJECTINSTANCE instance = { 0 }; - HRESULT result; + input_module_impl<dinput_device, osd_common_t>::input_init(machine); - // query the key name - instance.dwSize = sizeof(instance); - result = devinfo->dinput.device->GetObjectInfo(&instance, offset, DIPH_BYOFFSET); - - // if we got an error and have no default string, just return nullptr + HRESULT const result = m_dinput_helper->enum_attached_devices( + dinput_devclass(), + [this] (LPCDIDEVICEINSTANCE instance) { return device_enum_callback(instance); }); if (result != DI_OK) - { - if (defstring == nullptr) - return nullptr; - - // Return the default value - std::string result(defstring); - if (suffix) - result.append(" ").append(suffix); - return result; - } - - // convert the name to utf8 - std::string namestring = osd::text::from_tstring(instance.tszName); - - // if no suffix, return as-is - if (suffix) - namestring.append(" ").append(suffix); - return namestring; + fatalerror("DirectInput: Unable to enumerate devices (result=%08X)\n", uint32_t(result)); } protected: virtual int dinput_devclass() = 0; + virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) = 0; + + std::unique_ptr<dinput_api_helper> m_dinput_helper; }; + class keyboard_input_dinput : public dinput_module { public: @@ -158,42 +357,30 @@ public: { } - int dinput_devclass() override +protected: + virtual int dinput_devclass() override { return DI8DEVCLASS_KEYBOARD; } - BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override + virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) override { - running_machine &machine = *static_cast<running_machine *>(ref); - dinput_keyboard_device *devinfo; - int keynum; - // allocate and link in a new device - devinfo = m_dinput_helper->create_device<dinput_keyboard_device>(machine, *this, instance, &c_dfDIKeyboard, nullptr, dinput_cooperative_level::FOREGROUND); - if (devinfo == nullptr) - goto exit; - - // populate it - for (keynum = 0; keynum < MAX_KEYS; keynum++) - { - input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum); - char defname[20]; - std::string name; - - // generate/fetch the name - snprintf(defname, std::size(defname), "Scan%03d", keynum); - name = device_item_name(devinfo, keynum, defname, nullptr); - - // add the item to the device - devinfo->device()->add_item(name, itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]); - } + auto devinfo = m_dinput_helper->create_device<dinput_keyboard_device>( + *this, + instance, + &c_dfDIKeyboard, + nullptr, + background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND, + [] (auto...) { return true; }); + if (devinfo) + add_device(DEVICE_CLASS_KEYBOARD, std::move(devinfo)); - exit: return DIENUM_CONTINUE; } }; + class mouse_input_dinput : public dinput_module { public: @@ -202,68 +389,41 @@ public: { } - int dinput_devclass() override +protected: + virtual int dinput_devclass() override { return DI8DEVCLASS_POINTER; } - BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override + virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) override { - dinput_mouse_device *devinfo = nullptr; - running_machine &machine = *static_cast<running_machine *>(ref); - int axisnum, butnum; - HRESULT result; - // allocate and link in a new device - devinfo = m_dinput_helper->create_device<dinput_mouse_device>(machine, *this, instance, &c_dfDIMouse2, &c_dfDIMouse, dinput_cooperative_level::FOREGROUND); - if (!devinfo) - goto exit; - - // set relative mode on the mouse device - result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_REL); - if (result != DI_OK && result != DI_PROPNOEFFECT) - { - osd_printf_error("DirectInput: Unable to set relative mode for mouse %u (%s)\n", static_cast<unsigned int>(devicelist().size()), devinfo->name()); - goto error; - } - - // cap the number of axes and buttons based on the format - devinfo->dinput.caps.dwAxes = std::min(devinfo->dinput.caps.dwAxes, DWORD(3)); - devinfo->dinput.caps.dwButtons = std::min(devinfo->dinput.caps.dwButtons, DWORD((devinfo->dinput.format == &c_dfDIMouse) ? 4 : 8)); - - // populate the axes - for (axisnum = 0; axisnum < devinfo->dinput.caps.dwAxes; axisnum++) - { - // add to the mouse device and optionally to the gun device as well - std::string name = device_item_name(devinfo, offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr); - devinfo->device()->add_item( - name, - static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &devinfo->mouse.lX + axisnum); - } - - // populate the buttons - for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++) - { - auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIMOUSESTATE *>(nullptr)->rgbButtons[butnum]); - - // add to the mouse device - std::string name = device_item_name(devinfo, offset, default_button_name(butnum).c_str(), nullptr); - devinfo->device()->add_item( - name, - static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state<BYTE>, - &devinfo->mouse.rgbButtons[butnum]); - } + auto devinfo = m_dinput_helper->create_device<dinput_mouse_device>( + *this, + instance, + &c_dfDIMouse2, + &c_dfDIMouse, + background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND, + [] (auto const &device, auto const &format) -> bool + { + // set relative mode + HRESULT const result = dinput_api_helper::set_dword_property( + device, + DIPROP_AXISMODE, + 0, + DIPH_DEVICE, + DIPROPAXISMODE_REL); + if ((result != DI_OK) && (result != DI_PROPNOEFFECT)) + { + osd_printf_error("DirectInput: Unable to set relative mode for mouse.\n"); + return false; + } + return true; + }); + if (devinfo) + add_device(DEVICE_CLASS_MOUSE, std::move(devinfo)); - exit: return DIENUM_CONTINUE; - - error: - if (devinfo) - devicelist().free_device(*devinfo); - goto exit; } }; @@ -276,33 +436,40 @@ public: { } - int dinput_devclass() override +protected: + virtual int dinput_devclass() override { return DI8DEVCLASS_GAMECTRL; } - BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override + virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance) override { - dinput_cooperative_level cooperative_level = dinput_cooperative_level::FOREGROUND; - running_machine &machine = *static_cast<running_machine *>(ref); - dinput_joystick_device *devinfo; - int result = 0; - - if (!osd_common_t::s_window_list.empty() && osd_common_t::s_window_list.front()->win_has_menu()) - cooperative_level = dinput_cooperative_level::BACKGROUND; - // allocate and link in a new device - devinfo = m_dinput_helper->create_device<dinput_joystick_device>(machine, *this, instance, &c_dfDIJoystick, nullptr, cooperative_level); - if (devinfo == nullptr) - goto exit; - - result = devinfo->configure(); - if (result != 0) - { - osd_printf_error("Failed to configure DI Joystick device. Error 0x%x\n", static_cast<unsigned int>(result)); - } + auto devinfo = m_dinput_helper->create_device<dinput_joystick_device>( + *this, + instance, + &c_dfDIJoystick, + nullptr, + background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND, + [] (auto const &device, auto const &format) -> bool + { + // set absolute mode + HRESULT const result = dinput_api_helper::set_dword_property( + device, + DIPROP_AXISMODE, + 0, + DIPH_DEVICE, + DIPROPAXISMODE_ABS); + if ((result != DI_OK) && (result != DI_PROPNOEFFECT)) + { + osd_printf_error("DirectInput: Unable to set absolute mode for joystick.\n"); + return false; + } + return true; + }); + if (devinfo) + add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo)); - exit: return DIENUM_CONTINUE; } }; @@ -311,92 +478,64 @@ public: //============================================================ -// dinput_device - base directinput device +// dinput_device - base DirectInput device //============================================================ -dinput_device::dinput_device(running_machine &machine, std::string &&name, std::string &&id, input_device_class deviceclass, input_module &module) - : device_info(machine, std::move(name), std::move(id), deviceclass, module), - dinput({nullptr}) +dinput_device::dinput_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format) : + device_info(std::move(name), std::move(id), module), + m_device(std::move(device)), + m_caps(caps), + m_format(format) { } -dinput_device::~dinput_device() -{ - if (dinput.device) - dinput.device.Reset(); -} - HRESULT dinput_device::poll_dinput(LPVOID pState) const { HRESULT result; // first poll the device, then get the state - dinput.device->Poll(); - - // GetDeviceState returns the immediate state - result = dinput.device->GetDeviceState(dinput.format->dwDataSize, pState); + result = m_device->Poll(); // handle lost inputs here - if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) + if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED)) { - result = dinput.device->Acquire(); - if (result == DI_OK) - result = dinput.device->GetDeviceState(dinput.format->dwDataSize, pState); + result = m_device->Acquire(); + if ((result == DI_OK) || (result == S_FALSE)) + result = m_device->Poll(); } - return result; -} - - -//============================================================ -// dinput_keyboard_device - directinput keyboard device -//============================================================ - -dinput_keyboard_device::dinput_keyboard_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) - : dinput_device(machine, std::move(name), std::move(id), DEVICE_CLASS_KEYBOARD, module), - keyboard({{0}}) -{ -} - -// Polls the direct input immediate state -void dinput_keyboard_device::poll() -{ - std::lock_guard<std::mutex> scope_lock(m_device_lock); - - // Poll the state - dinput_device::poll_dinput(&keyboard.state); -} + // GetDeviceState returns the immediate state + if ((result == DI_OK) || (result == DI_NOEFFECT)) + result = m_device->GetDeviceState(m_format->dwDataSize, pState); -void dinput_keyboard_device::reset() -{ - memset(&keyboard.state, 0, sizeof(keyboard.state)); + return result; } - -//============================================================ -// dinput_mouse_device - directinput mouse device -//============================================================ - -dinput_mouse_device::dinput_mouse_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) - : dinput_device(machine, std::move(name), std::move(id), DEVICE_CLASS_MOUSE, module), - mouse({0}) +std::string dinput_device::item_name(int offset, std::string_view defstring, const char *suffix) const { -} + // query the key name + DIDEVICEOBJECTINSTANCE instance = { 0 }; + instance.dwSize = sizeof(instance); + HRESULT const result = m_device->GetObjectInfo(&instance, offset, DIPH_BYOFFSET); -void dinput_mouse_device::poll() -{ - // poll - dinput_device::poll_dinput(&mouse); + // use the default value if it failed + std::string name; + if (result != DI_OK) + name = defstring; + else + name = text::from_tstring(instance.tszName); - // scale the axis data - mouse.lX *= osd::INPUT_RELATIVE_PER_PIXEL; - mouse.lY *= osd::INPUT_RELATIVE_PER_PIXEL; - mouse.lZ *= osd::INPUT_RELATIVE_PER_PIXEL; -} + // if no suffix, return as-is + if (suffix) + name.append(" ").append(suffix); -void dinput_mouse_device::reset() -{ - memset(&mouse, 0, sizeof(mouse)); + return name; } @@ -404,63 +543,87 @@ void dinput_mouse_device::reset() // dinput_joystick_device - directinput joystick device //============================================================ -dinput_joystick_device::dinput_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) - : dinput_device(machine, std::move(name), std::move(id), DEVICE_CLASS_JOYSTICK, module), - joystick({{0}}) +dinput_joystick_device::dinput_joystick_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format) : + dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format), + m_joystick({ { 0 } }) { + // cap the number of axes, POVs, and buttons based on the format + m_caps.dwAxes = std::min(m_caps.dwAxes, DWORD(8)); + m_caps.dwPOVs = std::min(m_caps.dwPOVs, DWORD(4)); + m_caps.dwButtons = std::min(m_caps.dwButtons, DWORD(128)); } void dinput_joystick_device::reset() { - memset(&joystick.state, 0, sizeof(joystick.state)); + memset(&m_joystick.state, 0, sizeof(m_joystick.state)); + std::fill(std::begin(m_joystick.state.rgdwPOV), std::end(m_joystick.state.rgdwPOV), 0xffff); } -void dinput_joystick_device::poll() +void dinput_joystick_device::poll(bool relative_reset) { - int axisnum; - // poll the device first - if (dinput_device::poll_dinput(&joystick.state) != ERROR_SUCCESS) - return; - - // normalize axis values - for (axisnum = 0; axisnum < 8; axisnum++) + if (dinput_device::poll_dinput(&m_joystick.state) == DI_OK) { - LONG *axis = (&joystick.state.lX) + axisnum; - *axis = normalize_absolute_axis(*axis, joystick.rangemin[axisnum], joystick.rangemax[axisnum]); + // normalize axis values + for (int axisnum = 0; axisnum < 8; axisnum++) + { + auto const range = m_joystick.rangemax[axisnum] - m_joystick.rangemin[axisnum]; + if (range) + { + // assumes output range is symmetrical + LONG *const axis = &m_joystick.state.lX + axisnum; + double const offset = *axis - m_joystick.rangemin[axisnum]; + double const scaled = offset * double(input_device::ABSOLUTE_MAX - input_device::ABSOLUTE_MIN) / double(range); + *axis = lround(std::clamp<double>(scaled + input_device::ABSOLUTE_MIN, input_device::ABSOLUTE_MIN, input_device::ABSOLUTE_MAX)); + } + } } } -int dinput_joystick_device::configure() +void dinput_joystick_device::configure(input_device &device) { + input_device::assignment_vector assignments; HRESULT result; - auto &devicelist = static_cast<input_module_base&>(module()).devicelist(); - // temporary approximation of index - int devindex = devicelist.size(); - - // set absolute mode - result = dinput_set_dword_property(dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS); - if (result != DI_OK && result != DI_PROPNOEFFECT) - osd_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", devindex, name()); + // get device information - it gives clues about axis usage + DIDEVICEINSTANCE info; + info.dwSize = sizeof(info); + result = m_device->GetDeviceInfo(&info); + bool hid = false; + uint8_t type = DI8DEVTYPE_DEVICE; + uint8_t subtype = 0; + if (result == DI_OK) + { + hid = (info.dwDevType & DIDEVTYPE_HID) != 0; + type = info.dwDevType & 0x00ff; + subtype = (info.dwDevType >> 8) & 0x00ff; + osd_printf_verbose( + "DirectInput: Device type=0x%02X subtype=0x%02X HID=%u\n", + type, + subtype, + hid ? "yes" : "no"); + } // turn off deadzone; we do our own calculations - result = dinput_set_dword_property(dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0); - if (result != DI_OK && result != DI_PROPNOEFFECT) - osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", devindex, name()); + result = dinput_api_helper::set_dword_property(m_device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0); + if ((result != DI_OK) && (result != DI_PROPNOEFFECT)) + osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %s.\n", name()); // turn off saturation; we do our own calculations - result = dinput_set_dword_property(dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000); - if (result != DI_OK && result != DI_PROPNOEFFECT) - osd_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", devindex, name()); - - // cap the number of axes, POVs, and buttons based on the format - dinput.caps.dwAxes = std::min(dinput.caps.dwAxes, DWORD(8)); - dinput.caps.dwPOVs = std::min(dinput.caps.dwPOVs, DWORD(4)); - dinput.caps.dwButtons = std::min(dinput.caps.dwButtons, DWORD(128)); + result = dinput_api_helper::set_dword_property(m_device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000); + if ((result != DI_OK) && (result != DI_PROPNOEFFECT)) + osd_printf_warning("DirectInput: Unable to reset saturation for joystick %s.\n", name()); // populate the axes - for (uint32_t axisnum = 0, axiscount = 0; axiscount < dinput.caps.dwAxes && axisnum < 8; axisnum++) + input_item_id axisitems[8]; + std::fill(std::begin(axisitems), std::end(axisitems), ITEM_ID_INVALID); + for (uint32_t axisnum = 0, axiscount = 0; (axiscount < m_caps.dwAxes) && (axisnum < 8); axisnum++) { // fetch the range of this axis DIPROPRANGE dipr; @@ -468,62 +631,555 @@ int dinput_joystick_device::configure() dipr.diph.dwHeaderSize = sizeof(dipr.diph); dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG); dipr.diph.dwHow = DIPH_BYOFFSET; - result = dinput.device->GetProperty(DIPROP_RANGE, &dipr.diph); + result = m_device->GetProperty(DIPROP_RANGE, &dipr.diph); if (result != DI_OK) + { + // this is normal when axes are skipped, e.g. X/Y/Z present, rX/rY absent, rZ present + osd_printf_verbose("DirectInput: Unable to get properties for joystick %s axis %u.\n", name(), axisnum); continue; + } - joystick.rangemin[axisnum] = dipr.lMin; - joystick.rangemax[axisnum] = dipr.lMax; + m_joystick.rangemin[axisnum] = dipr.lMin; + m_joystick.rangemax[axisnum] = dipr.lMax; // populate the item description as well - std::string name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr); - device()->add_item( - name, - static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &joystick.state.lX + axisnum); + axisitems[axisnum] = device.add_item( + item_name(offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr), + std::string_view(), + input_item_id(ITEM_ID_XAXIS + axisnum), + generic_axis_get_state<LONG>, + &m_joystick.state.lX + axisnum); axiscount++; } - // populate the POVs - for (uint32_t povnum = 0; povnum < dinput.caps.dwPOVs; povnum++) + // take a guess at which axes might be pedals depending on type and remap onto negative half of range + input_item_id pedalitems[3] = { ITEM_ID_INVALID, ITEM_ID_INVALID, ITEM_ID_INVALID }; + if (DI8DEVTYPE_FLIGHT == type) { - std::string name; + // Rx/Ry are often used for brakes + bool const rxpedal = (ITEM_ID_INVALID != axisitems[3]) && !m_joystick.rangemin[3] && (0 < m_joystick.rangemax[3]); + bool const rypedal = (ITEM_ID_INVALID != axisitems[4]) && !m_joystick.rangemin[4] && (0 < m_joystick.rangemax[4]); + if (rxpedal && rypedal) + { + pedalitems[0] = axisitems[3]; + m_joystick.rangemin[3] = m_joystick.rangemax[3]; + m_joystick.rangemax[3] = -m_joystick.rangemax[3]; + pedalitems[1] = axisitems[4]; + m_joystick.rangemin[4] = m_joystick.rangemax[4]; + m_joystick.rangemax[4] = -m_joystick.rangemax[4]; + } + } + else if (DI8DEVTYPE_DRIVING == type) + { + bool const ypedal = (ITEM_ID_INVALID != axisitems[1]) && !m_joystick.rangemin[1] && (0 < m_joystick.rangemax[1]); + bool const rxpedal = (ITEM_ID_INVALID != axisitems[3]) && !m_joystick.rangemin[3] && (0 < m_joystick.rangemax[3]); + bool const rypedal = (ITEM_ID_INVALID != axisitems[4]) && !m_joystick.rangemin[4] && (0 < m_joystick.rangemax[4]); + bool const rzpedal = (ITEM_ID_INVALID != axisitems[5]) && !m_joystick.rangemin[5] && (0 < m_joystick.rangemax[5]); + bool const s0pedal = (ITEM_ID_INVALID != axisitems[6]) && !m_joystick.rangemin[6] && (0 < m_joystick.rangemax[6]); + if (DI8DEVTYPEDRIVING_DUALPEDALS == subtype) + { + // dual pedals are usually Y and Rz + if (ypedal && rzpedal) + { + pedalitems[0] = axisitems[1]; + m_joystick.rangemin[1] = m_joystick.rangemax[1]; + m_joystick.rangemax[1] = -m_joystick.rangemax[1]; + + pedalitems[1] = axisitems[5]; + m_joystick.rangemin[5] = m_joystick.rangemax[5]; + m_joystick.rangemax[5] = -m_joystick.rangemax[5]; + } + } + else if (DI8DEVTYPEDRIVING_THREEPEDALS == subtype) + { + // triple pedals may be Y, Rz and slider 0, or Rx, Ry and Rz + if (ypedal && rzpedal && s0pedal) + { + pedalitems[0] = axisitems[1]; + m_joystick.rangemin[1] = m_joystick.rangemax[1]; + m_joystick.rangemax[1] = -m_joystick.rangemax[1]; + + pedalitems[1] = axisitems[5]; + m_joystick.rangemin[5] = m_joystick.rangemax[5]; + m_joystick.rangemax[5] = -m_joystick.rangemax[5]; + + pedalitems[2] = axisitems[6]; + m_joystick.rangemin[6] = m_joystick.rangemax[6]; + m_joystick.rangemax[6] = -m_joystick.rangemax[6]; + } + else if (rxpedal && rypedal && rzpedal) + { + pedalitems[0] = axisitems[3]; + m_joystick.rangemin[3] = m_joystick.rangemax[3]; + m_joystick.rangemax[3] = -m_joystick.rangemax[3]; + + pedalitems[1] = axisitems[4]; + m_joystick.rangemin[4] = m_joystick.rangemax[4]; + m_joystick.rangemax[4] = -m_joystick.rangemax[4]; + + pedalitems[2] = axisitems[5]; + m_joystick.rangemin[5] = m_joystick.rangemax[5]; + m_joystick.rangemax[5] = -m_joystick.rangemax[5]; + } + } + } + + // populate the POV hats + input_item_id povitems[4][4]; + for (auto &pov : povitems) + std::fill(std::begin(pov), std::end(pov), ITEM_ID_INVALID); + for (uint32_t povnum = 0; povnum < m_caps.dwPOVs; povnum++) + { // left - name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum).c_str(), "Left"); - device()->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_LEFT))); + povitems[povnum][0] = device.add_item( + item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Left"), + std::string_view(), + input_item_id((povnum * 4) + ITEM_ID_HAT1LEFT), + &dinput_joystick_device::pov_get_state, + reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_LEFT))); // right - name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum).c_str(), "Right"); - device()->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_RIGHT))); + povitems[povnum][1] = device.add_item( + item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Right"), + std::string_view(), + input_item_id((povnum * 4) + ITEM_ID_HAT1RIGHT), + &dinput_joystick_device::pov_get_state, + reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_RIGHT))); // up - name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum).c_str(), "Up"); - device()->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_UP))); + povitems[povnum][2] = device.add_item( + item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Up"), + std::string_view(), + input_item_id((povnum * 4) + ITEM_ID_HAT1UP), + &dinput_joystick_device::pov_get_state, + reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_UP))); // down - name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum).c_str(), "Down"); - device()->add_item(name, ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<uintptr_t>(povnum * 4 + POVDIR_DOWN))); + povitems[povnum][3] = device.add_item( + item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Down"), + std::string_view(), + input_item_id((povnum * 4) + ITEM_ID_HAT1DOWN), + &dinput_joystick_device::pov_get_state, + reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_DOWN))); } // populate the buttons - for (uint32_t butnum = 0; butnum < dinput.caps.dwButtons; butnum++) + for (uint32_t butnum = 0; butnum < m_caps.dwButtons; butnum++) { auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIJOYSTATE2 *>(nullptr)->rgbButtons[butnum]); - std::string name = dinput_module::device_item_name(this, offset, default_button_name(butnum).c_str(), nullptr); input_item_id itemid; - if (butnum < INPUT_MAX_BUTTONS) - itemid = static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum); + itemid = input_item_id(ITEM_ID_BUTTON1 + butnum); else if (butnum < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) - itemid = static_cast<input_item_id>(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + butnum); + itemid = input_item_id(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + butnum); else itemid = ITEM_ID_OTHER_SWITCH; - device()->add_item(name, itemid, generic_button_get_state<BYTE>, &joystick.state.rgbButtons[butnum]); + input_item_id const actual = device.add_item( + item_name(offset, default_button_name(butnum), nullptr), + std::string_view(), + itemid, + generic_button_get_state<BYTE>, + &m_joystick.state.rgbButtons[butnum]); + + // there are sixteen action button types + if (butnum < 16) + { + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual)); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + butnum), SEQ_TYPE_STANDARD, seq); + + // assign the first few buttons to UI actions and pedals + // TODO: don't map pedals for driving controls that have them present + switch (butnum) + { + case 0: + assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq); + break; + case 1: + assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back((3 > m_caps.dwButtons) ? IPT_UI_CLEAR : IPT_UI_BACK, SEQ_TYPE_STANDARD, seq); + break; + case 2: + assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq); + break; + case 3: + assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq); + break; + } + } + } + + // add default assignments depending on type + if (DI8DEVTYPE_FLIGHT == type) + { + if (((ITEM_ID_INVALID == axisitems[0]) || (ITEM_ID_INVALID == axisitems[1])) && (1 <= m_caps.dwPOVs)) + { + // X or Y missing, fall back to using POV hat for navigation + add_directional_assignments( + assignments, + axisitems[0], + axisitems[1], + povitems[0][0], + povitems[0][1], + povitems[0][2], + povitems[0][3]); + + // try using throttle for zoom/focus + if (ITEM_ID_INVALID != axisitems[2]) + { + input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[2])); + input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[2])); + if (ITEM_ID_INVALID != axisitems[5]) + { + assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq); + assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq); + assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq); + assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq); + } + else + { + assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, posseq); + assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, negseq); + } + } + + // try using twist/rudder for next/previous group + if (ITEM_ID_INVALID != axisitems[5]) + { + input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[5])); + input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[5])); + assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, negseq); + assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, posseq); + } + } + else + { + // only use stick for primary navigation/movement + add_directional_assignments( + assignments, + axisitems[0], + axisitems[1], + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID); + + // try using hat for secondary navigation functions + if (1 <= m_caps.dwPOVs) + { + input_seq const leftseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][0])); + input_seq const rightseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][1])); + input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][2])); + input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][3])); + if ((ITEM_ID_INVALID != axisitems[2]) || (ITEM_ID_INVALID != axisitems[5])) + { + assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, leftseq); + assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, rightseq); + assignments.emplace_back(IPT_UI_PAGE_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_UI_PAGE_DOWN, SEQ_TYPE_STANDARD, downseq); + } + else + { + assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, leftseq); + assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, rightseq); + assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, leftseq); + assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, rightseq); + assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, downseq); + } + } + + // try using throttle for zoom + if (ITEM_ID_INVALID != axisitems[2]) + { + input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[2])); + input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[2])); + if ((1 <= m_caps.dwPOVs) || (ITEM_ID_INVALID != axisitems[5])) + { + assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq); + assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq); + if ((1 > m_caps.dwPOVs) || (ITEM_ID_INVALID == axisitems[5])) + { + assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq); + assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq); + } + } + else + { + assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, posseq); + assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, negseq); + } + } + + // try using twist/rudder for focus next/previous + if (ITEM_ID_INVALID != axisitems[5]) + { + input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[5])); + input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[5])); + if (1 <= m_caps.dwPOVs) + { + assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq); + assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq); + if (ITEM_ID_INVALID == axisitems[2]) + { + assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq); + assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq); + } + } + else + { + assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, negseq); + assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, posseq); + } + } + } + + // Z or slider 0 is usually the throttle - use one of them for joystick Z + add_assignment( + assignments, + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NONE, + { axisitems[2], axisitems[6] }); + + // use Z for the first two pedals if present + if (ITEM_ID_INVALID != axisitems[2]) + { + // TODO: use Rx/Ry as well if they appear to be brakes + input_seq const pedal1seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axisitems[2])); + input_seq const pedal2seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axisitems[2])); + assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_STANDARD, pedal1seq); + assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_STANDARD, pedal2seq); + } + } + else if (DI8DEVTYPE_DRIVING == type) + { + // use the wheel and D-pad for navigation and directional controls + add_directional_assignments( + assignments, + axisitems[0], + ITEM_ID_INVALID, + povitems[0][0], + povitems[0][1], + povitems[0][2], + povitems[0][3]); + + // check subtype to determine how pedals should be assigned + if (DI8DEVTYPEDRIVING_COMBINEDPEDALS == subtype) + { + if (ITEM_ID_INVALID != axisitems[1]) + { + // put first two pedals on opposite sides of Y axis + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axisitems[1]))); + assignments.emplace_back( + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axisitems[1]))); + + // use for previous/next group as well + assignments.emplace_back( + IPT_UI_NEXT_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[1]))); + assignments.emplace_back( + IPT_UI_PREV_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[1]))); + } + } + else + { + // see if we have individual pedals + if (ITEM_ID_INVALID != pedalitems[0]) + { + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[0]))); + assignments.emplace_back( + IPT_UI_NEXT_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[0]))); + } + if (ITEM_ID_INVALID != pedalitems[1]) + { + assignments.emplace_back( + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[1]))); + assignments.emplace_back( + IPT_UI_PREV_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[1]))); + } + if (ITEM_ID_INVALID != pedalitems[2]) + { + assignments.emplace_back( + IPT_PEDAL3, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[2]))); + assignments.emplace_back( + IPT_UI_FOCUS_NEXT, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[2]))); + } + } + } + else + { + // assume this is a gamepad - see if it looks like it has dual analog sticks + input_item_id stickaxes[2][2] = { + { axisitems[0], axisitems[1] }, + { ITEM_ID_INVALID, ITEM_ID_INVALID } }; + input_item_id pedalaxis = ITEM_ID_INVALID; + if ((ITEM_ID_INVALID != axisitems[2]) && (ITEM_ID_INVALID != axisitems[5])) + { + // assume Z/Rz are right stick + stickaxes[1][0] = axisitems[2]; + stickaxes[1][1] = axisitems[5]; + add_twin_stick_assignments( + assignments, + stickaxes[0][0], + stickaxes[0][1], + stickaxes[1][0], + stickaxes[1][1], + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID); + } + else if ((ITEM_ID_INVALID != axisitems[3]) && (ITEM_ID_INVALID != axisitems[4])) + { + // assume Rx/Ry are right stick and Z is triggers if present + stickaxes[1][0] = axisitems[3]; + stickaxes[1][1] = axisitems[4]; + pedalaxis = axisitems[2]; + add_twin_stick_assignments( + assignments, + stickaxes[0][0], + stickaxes[0][1], + stickaxes[1][0], + stickaxes[1][1], + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID); + } + else + { + // if Z is present, use it as secondary Y + stickaxes[1][1] = axisitems[2]; + } + + // try to find a "complete" stick for primary movement controls + input_item_id diraxis[2][2]; + choose_primary_stick(diraxis, stickaxes[0][0], stickaxes[0][1], stickaxes[1][0], stickaxes[1][1]); + add_directional_assignments( + assignments, + diraxis[0][0], + diraxis[0][1], + povitems[0][0], + povitems[0][1], + povitems[0][2], + povitems[0][3]); + + // assign a secondary stick axis to joystick Z + add_assignment( + assignments, + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NONE, + { diraxis[1][1], diraxis[1][0] }); + + // try to find a suitable axis to use for the first two pedals + add_assignment( + assignments, + IPT_PEDAL, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NEG, + { pedalaxis, diraxis[1][1], diraxis[0][1] }); + add_assignment( + assignments, + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_POS, + { pedalaxis, diraxis[1][1], diraxis[0][1] }); + + // try to choose an axis for previous/next group + if (ITEM_ID_INVALID != pedalaxis) + { + // this is reversed because right trigger is negative direction + assignments.emplace_back( + IPT_UI_PREV_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, pedalaxis))); + assignments.emplace_back( + IPT_UI_NEXT_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalaxis))); + pedalaxis = ITEM_ID_INVALID; + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1])) + { + // took secondary Y + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][0])) + { + // took secondary X + } + + // use secondary Y for page up/down if available + consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, diraxis[1][1]); + + // put focus previous/next and zoom on secondary X if available + add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][0]); + add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, diraxis[1][0]); + } + + // set default assignments + device.set_default_assignments(std::move(assignments)); +} + +int32_t dinput_joystick_device::pov_get_state(void *device_internal, void *item_internal) +{ + auto *const devinfo = static_cast<dinput_joystick_device *>(device_internal); + int const povnum = uintptr_t(item_internal) / 4; + int const povdir = uintptr_t(item_internal) % 4; + + // get the current state + DWORD const pov = devinfo->m_joystick.state.rgdwPOV[povnum]; + + // if invalid, return 0 + if ((pov & 0xffff) == 0xffff) + return 0; + + // return the current state + switch (povdir) + { + case POVDIR_LEFT: return (pov >= 22500) && (pov <= 31500); + case POVDIR_RIGHT: return (pov >= 4500) && (pov <= 13500); + case POVDIR_UP: return (pov >= 31500) || (pov <= 4500); + case POVDIR_DOWN: return (pov >= 13500) && (pov <= 22500); } return 0; @@ -535,18 +1191,16 @@ int dinput_joystick_device::configure() //============================================================ dinput_api_helper::dinput_api_helper() - : m_dinput(nullptr) { } dinput_api_helper::~dinput_api_helper() { - m_dinput.Reset(); } int dinput_api_helper::initialize() { - HRESULT result = DirectInput8Create(GetModuleHandleUni(), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<void **>(m_dinput.GetAddressOf()), nullptr); + HRESULT result = DirectInput8Create(GetModuleHandleUni(), DIRECTINPUT_VERSION, IID_IDirectInput8, &m_dinput, nullptr); if (result != DI_OK) { return result; @@ -557,25 +1211,132 @@ int dinput_api_helper::initialize() } -HRESULT dinput_api_helper::enum_attached_devices(int devclass, device_enum_interface *enumerate_interface, void *state) const +std::pair<Microsoft::WRL::ComPtr<IDirectInputDevice8>, LPCDIDATAFORMAT> dinput_api_helper::open_device( + LPCDIDEVICEINSTANCE instance, + LPCDIDATAFORMAT format1, + LPCDIDATAFORMAT format2, + dinput_cooperative_level cooperative_level) { - device_enum_interface::dinput_callback_context ctx; - ctx.self = enumerate_interface; - ctx.state = state; + HRESULT result; - return m_dinput->EnumDevices(devclass, device_enum_interface::enum_callback, &ctx, DIEDFL_ATTACHEDONLY); + // attempt to create a device + Microsoft::WRL::ComPtr<IDirectInputDevice8> device; + result = m_dinput->CreateDevice(instance->guidInstance, &device, nullptr); + if (result != DI_OK) + { + osd_printf_error("DirectInput: Unable to create device.\n"); + return std::make_pair(nullptr, nullptr); + } + + // attempt to set the data format + LPCDIDATAFORMAT format = format1; + result = device->SetDataFormat(format); + if ((result != DI_OK) && format2) + { + // use the secondary format if available + osd_printf_verbose("DirectInput: Error setting primary data format, trying secondary format.\n"); + format = format2; + result = device->SetDataFormat(format); + } + if (result != DI_OK) + { + osd_printf_error("DirectInput: Unable to set data format.\n"); + return std::make_pair(nullptr, nullptr); + } + + // default window to the first window in the list + // For now, we always use the desktop window due to multiple issues: + // * MAME recreates windows on toggling fullscreen. DirectInput really doesn't like this. + // * DirectInput doesn't like the window used for D3D fullscreen exclusive mode. + // * With multiple windows, the first window needs to have focus when using foreground mode. + // This makes it impossible to use force feedback as that requires foreground exclusive mode. + // The only way to get around this would be to reopen devices on focus changes. + [[maybe_unused]] HWND window_handle; + DWORD di_cooperative_level; +#if defined(OSD_WINDOWS) + auto const &window = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()); + bool const standalone_window = !window.attached_mode(); +#elif defined(SDLMAME_WIN32) + auto const &window = dynamic_cast<sdl_window_info &>(*osd_common_t::window_list().front()); + bool const standalone_window = true; +#endif + if (!standalone_window) + { + // in attached mode we have to ignore the caller and hook up to the desktop window + window_handle = GetDesktopWindow(); + di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; + } + else + { +#if defined(OSD_WINDOWS) + window_handle = window.platform_window(); +#elif defined(SDLMAME_WIN32) + auto const sdlwindow = window.platform_window(); + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (!SDL_GetWindowWMInfo(sdlwindow, &info)) + return std::make_pair(nullptr, nullptr); + window_handle = info.info.win.window; +#endif + switch (cooperative_level) + { + case dinput_cooperative_level::BACKGROUND: + di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; + break; + case dinput_cooperative_level::FOREGROUND: + //di_cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE; + di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; + break; + default: + throw false; + } + } + + // set the cooperative level + result = device->SetCooperativeLevel(GetDesktopWindow(), di_cooperative_level); + if (result != DI_OK) + { + osd_printf_error("DirectInput: Unable to set cooperative level.\n"); + return std::make_pair(nullptr, nullptr); + } + + // return new device + return std::make_pair(std::move(device), format); +} + + +std::string dinput_api_helper::make_id(LPCDIDEVICEINSTANCE instance) +{ + // use name, product GUID and instance GUID as identifier + return + text::from_tstring(instance->tszInstanceName) + + " product_" + + guid_to_string(instance->guidProduct) + + " instance_" + + guid_to_string(instance->guidInstance); } -#else // defined(OSD_WINDOWS) +} // namespace osd + + +#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include "input_module.h" +namespace osd { + +namespace { + MODULE_NOT_SUPPORTED(keyboard_input_dinput, OSD_KEYBOARDINPUT_PROVIDER, "dinput") MODULE_NOT_SUPPORTED(mouse_input_dinput, OSD_MOUSEINPUT_PROVIDER, "dinput") MODULE_NOT_SUPPORTED(joystick_input_dinput, OSD_JOYSTICKINPUT_PROVIDER, "dinput") -#endif // defined(OSD_WINDOWS) +} // anonymous namespace + +} // namespace osd + +#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -MODULE_DEFINITION(KEYBOARDINPUT_DINPUT, keyboard_input_dinput) -MODULE_DEFINITION(MOUSEINPUT_DINPUT, mouse_input_dinput) -MODULE_DEFINITION(JOYSTICKINPUT_DINPUT, joystick_input_dinput) +MODULE_DEFINITION(KEYBOARDINPUT_DINPUT, osd::keyboard_input_dinput) +MODULE_DEFINITION(MOUSEINPUT_DINPUT, osd::mouse_input_dinput) +MODULE_DEFINITION(JOYSTICKINPUT_DINPUT, osd::joystick_input_dinput) diff --git a/src/osd/modules/input/input_dinput.h b/src/osd/modules/input/input_dinput.h index c9e84d1f633..98fdfb011bb 100644 --- a/src/osd/modules/input/input_dinput.h +++ b/src/osd/modules/input/input_dinput.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles, Brad Hughes +// copyright-holders:Aaron Giles, Brad Hughes, Vas Crabb //============================================================ // // input_dinput.h - Windows DirectInput support @@ -10,7 +10,8 @@ #pragma once -#include "input_windows.h" +#include "assignmenthelper.h" +#include "input_wincommon.h" #include "modules/lib/osdlib.h" #include "modules/lib/osdobj_common.h" @@ -19,40 +20,24 @@ #include "strconv.h" -#include <dinput.h> +#include <memory> +#include <mutex> +#include <string> +#include <string_view> +#include <type_traits> +#include <utility> + #include <windows.h> +#include <dinput.h> #include <wrl/client.h> +namespace osd { + //============================================================ // dinput_device - base directinput device //============================================================ -class device_enum_interface -{ -public: - struct dinput_callback_context - { - device_enum_interface * self; - void * state; - }; - - virtual ~device_enum_interface() - { - } - - static BOOL CALLBACK enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) - { - auto context = static_cast<dinput_callback_context*>(ref); - return context->self->device_enum_callback(instance, context->state); - } - - virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) = 0; -}; - -// Typedef for dynamically loaded function -typedef HRESULT (WINAPI *dinput_create_fn)(HINSTANCE, DWORD, LPDIRECTINPUT8 *, LPUNKNOWN); - enum class dinput_cooperative_level { FOREGROUND, @@ -61,181 +46,144 @@ enum class dinput_cooperative_level class dinput_api_helper { -private: - Microsoft::WRL::ComPtr<IDirectInput8> m_dinput; - osd::dynamic_module::ptr m_dinput_dll; - public: dinput_api_helper(); - virtual ~dinput_api_helper(); + ~dinput_api_helper(); + int initialize(); - template <class TDevice> - TDevice *create_device( - running_machine &machine, + template <typename TDevice, typename TCallback> + std::unique_ptr<TDevice> create_device( input_module_base &module, LPCDIDEVICEINSTANCE instance, LPCDIDATAFORMAT format1, LPCDIDATAFORMAT format2, - dinput_cooperative_level cooperative_level) + dinput_cooperative_level cooperative_level, + TCallback &&callback) { - HRESULT result; - std::shared_ptr<win_window_info> window; - HWND hwnd; - - // convert instance name to utf8 - std::string utf8_instance_name = osd::text::from_tstring(instance->tszInstanceName); - - // set device id to name + product unique identifier + instance unique identifier - std::string utf8_instance_id = utf8_instance_name + " product_" + guid_to_string(instance->guidProduct) + " instance_" + guid_to_string(instance->guidInstance); - - // allocate memory for the device object - TDevice &devinfo = module.devicelist().create_device<TDevice>(machine, std::move(utf8_instance_name), std::move(utf8_instance_id), module); - - // attempt to create a device - result = m_dinput->CreateDevice(instance->guidInstance, devinfo.dinput.device.GetAddressOf(), nullptr); + auto [device, format] = open_device(instance, format1, format2, cooperative_level); + if (!device) + return nullptr; + + // get the capabilities + DIDEVCAPS caps; + caps.dwSize = sizeof(caps); + HRESULT const result = device->GetCapabilities(&caps); if (result != DI_OK) - goto error; + return nullptr; - // get the caps - devinfo.dinput.caps.dwSize = sizeof(devinfo.dinput.caps); - result = devinfo.dinput.device->GetCapabilities(&devinfo.dinput.caps); - if (result != DI_OK) - goto error; + if (!callback(device, format)) + return nullptr; - // attempt to set the data format - devinfo.dinput.format = format1; - result = devinfo.dinput.device->SetDataFormat(devinfo.dinput.format); - if (result != DI_OK) - { - // use the secondary format if available - if (format2) - { - devinfo.dinput.format = format2; - result = devinfo.dinput.device->SetDataFormat(devinfo.dinput.format); - } - if (result != DI_OK) - goto error; - } - - // default window to the first window in the list - window = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front()); - DWORD di_cooperative_level; - if (window->attached_mode()) - { - // in attached mode we have to ignore the caller and hook up to the desktop window - hwnd = GetDesktopWindow(); - di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; - } - else - { - hwnd = window->platform_window(); - switch (cooperative_level) - { - case dinput_cooperative_level::BACKGROUND: - di_cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; - break; - case dinput_cooperative_level::FOREGROUND: - di_cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE; - break; - default: - throw false; - } - } - - // set the cooperative level - result = devinfo.dinput.device->SetCooperativeLevel(hwnd, di_cooperative_level); - if (result != DI_OK) - goto error; + // allocate memory for the device object + return std::make_unique<TDevice>( + text::from_tstring(instance->tszInstanceName), + make_id(instance), + module, + std::move(device), + caps, + format); + } - return &devinfo; + template <typename T> + HRESULT enum_attached_devices(int devclass, T &&callback) const + { + return m_dinput->EnumDevices( + devclass, + &di_enum_devices_cb<std::remove_reference_t<T> >, + LPVOID(&callback), + DIEDFL_ATTACHEDONLY); + } - error: - module.devicelist().free_device(devinfo); - return nullptr; + template <typename T> + static HRESULT set_dword_property( + T &&device, + REFGUID property_guid, + DWORD object, + DWORD how, + DWORD value) + { + DIPROPDWORD dipdw; + dipdw.diph.dwSize = sizeof(dipdw); + dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); + dipdw.diph.dwObj = object; + dipdw.diph.dwHow = how; + dipdw.dwData = value; + + return device->SetProperty(property_guid, &dipdw.diph); } - HRESULT enum_attached_devices(int devclass, device_enum_interface *enumerate_interface, void *state) const; +private: + std::pair<Microsoft::WRL::ComPtr<IDirectInputDevice8>, LPCDIDATAFORMAT> open_device( + LPCDIDEVICEINSTANCE instance, + LPCDIDATAFORMAT format1, + LPCDIDATAFORMAT format2, + dinput_cooperative_level cooperative_level); - static std::string guid_to_string(const GUID& guid) + static std::string make_id(LPCDIDEVICEINSTANCE instance); + + template <typename T> + static BOOL CALLBACK di_enum_devices_cb(LPCDIDEVICEINSTANCE instance, LPVOID ref) { - // Size of a GUID string with dashes plus null terminator - char guid_string[37]; - - snprintf( - guid_string, std::size(guid_string), - "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", - guid.Data1, guid.Data2, guid.Data3, - guid.Data4[0], guid.Data4[1], guid.Data4[2], - guid.Data4[3], guid.Data4[4], guid.Data4[5], - guid.Data4[6], guid.Data4[7]); - - return guid_string; + return (*reinterpret_cast<T *>(ref))(instance); } + + Microsoft::WRL::ComPtr<IDirectInput8> m_dinput; + dynamic_module::ptr m_dinput_dll; }; + class dinput_device : public device_info { -public: - // DirectInput-specific information about a device - struct dinput_api_state - { - Microsoft::WRL::ComPtr<IDirectInputDevice8> device; - DIDEVCAPS caps; - LPCDIDATAFORMAT format; - }; +protected: + dinput_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format); - dinput_api_state dinput; + HRESULT poll_dinput(LPVOID pState) const; - dinput_device(running_machine &machine, std::string &&name, std::string &&id, input_device_class deviceclass, input_module &module); - virtual ~dinput_device(); + std::string item_name(int offset, std::string_view defstr, char const *suffix) const; -protected: - HRESULT poll_dinput(LPVOID pState) const; + // DirectInput-specific information about a device + Microsoft::WRL::ComPtr<IDirectInputDevice8> const m_device; + DIDEVCAPS m_caps; + LPCDIDATAFORMAT const m_format; }; -class dinput_keyboard_device : public dinput_device -{ -private: - std::mutex m_device_lock; +class dinput_joystick_device : public dinput_device, protected joystick_assignment_helper +{ public: - keyboard_state keyboard; - - dinput_keyboard_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module); + dinput_joystick_device( + std::string &&name, + std::string &&id, + input_module &module, + Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device, + DIDEVCAPS const &caps, + LPCDIDATAFORMAT format); - void poll() override; void reset() override; -}; + void poll(bool relative_reset) override; + void configure(input_device &device) override; -class dinput_mouse_device : public dinput_device -{ -public: - mouse_state mouse; +private: + // state information for a joystick; DirectInput state must be first element + struct dinput_joystick_state + { + DIJOYSTATE state; + LONG rangemin[8]; + LONG rangemax[8]; + }; -public: - dinput_mouse_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module); - void poll() override; - void reset() override; -}; + static int32_t pov_get_state(void *device_internal, void *item_internal); -// state information for a joystick; DirectInput state must be first element -struct dinput_joystick_state -{ - DIJOYSTATE state; - LONG rangemin[8]; - LONG rangemax[8]; + dinput_joystick_state m_joystick; }; -class dinput_joystick_device : public dinput_device -{ -public: - dinput_joystick_state joystick; -public: - dinput_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module); - void reset() override; - void poll() override; - int configure(); -}; +} // namespace osd #endif // MAME_OSD_INPUT_INPUT_DINPUT_H diff --git a/src/osd/modules/input/input_mac.cpp b/src/osd/modules/input/input_mac.cpp index 85afdaeb9cb..90360adaa3f 100644 --- a/src/osd/modules/input/input_mac.cpp +++ b/src/osd/modules/input/input_mac.cpp @@ -31,17 +31,10 @@ #include "../../mac/osdmac.h" #include "input_common.h" -extern void MacPollInputs(); - void mac_osd_interface::customize_input_type_list(std::vector<input_type_entry> &typelist) { } -void mac_osd_interface::poll_inputs(running_machine &machine) -{ - MacPollInputs(); -} - void mac_osd_interface::release_keys() { } diff --git a/src/osd/modules/input/input_module.h b/src/osd/modules/input/input_module.h index 732b9593a57..64e5ebb2332 100644 --- a/src/osd/modules/input/input_module.h +++ b/src/osd/modules/input/input_module.h @@ -15,20 +15,13 @@ #include "modules/osdmodule.h" -class input_module : public osd_module +class input_module { public: - input_module(const char *type, const char *name) - : osd_module(type, name) - { - } - - virtual ~input_module() { } + virtual ~input_module() = default; virtual void input_init(running_machine &machine) = 0; - virtual void poll_if_necessary(running_machine &machine) = 0; - virtual void pause() = 0; - virtual void resume() = 0; + virtual void poll_if_necessary(bool relative_reset) = 0; }; //============================================================ diff --git a/src/osd/modules/input/input_none.cpp b/src/osd/modules/input/input_none.cpp index bbe19661ab6..96c3f116868 100644 --- a/src/osd/modules/input/input_none.cpp +++ b/src/osd/modules/input/input_none.cpp @@ -7,56 +7,58 @@ //============================================================ #include "input_module.h" -#include "modules/osdmodule.h" -class keyboard_input_none : public input_module + +namespace osd { + +namespace { + +class keyboard_input_none : public osd_module, public input_module { public: - keyboard_input_none() : input_module(OSD_KEYBOARDINPUT_PROVIDER, "none") { } - int init(const osd_options &options) override { return 0; } - void poll_if_necessary(running_machine &machine) override { } + keyboard_input_none() : osd_module(OSD_KEYBOARDINPUT_PROVIDER, "none") { } + int init(osd_interface &osd, const osd_options &options) override { return 0; } void input_init(running_machine &machine) override { } - void pause() override { } - void resume() override { } + void poll_if_necessary(bool relative_reset) override { } }; -MODULE_DEFINITION(KEYBOARD_NONE, keyboard_input_none) -class mouse_input_none : public input_module +class mouse_input_none : public osd_module, public input_module { public: - mouse_input_none() : input_module(OSD_MOUSEINPUT_PROVIDER, "none") { } - int init(const osd_options &options) override { return 0; } + mouse_input_none() : osd_module(OSD_MOUSEINPUT_PROVIDER, "none") { } + int init(osd_interface &osd, const osd_options &options) override { return 0; } void input_init(running_machine &machine) override { } - void poll_if_necessary(running_machine &machine) override { } - void pause() override { } - void resume() override { } + void poll_if_necessary(bool relative_reset) override { } }; -MODULE_DEFINITION(MOUSE_NONE, mouse_input_none) -class lightgun_input_none : public input_module +class lightgun_input_none : public osd_module, public input_module { public: - lightgun_input_none() : input_module(OSD_LIGHTGUNINPUT_PROVIDER, "none") { } - int init(const osd_options &options) override { return 0; } + lightgun_input_none() : osd_module(OSD_LIGHTGUNINPUT_PROVIDER, "none") { } + int init(osd_interface &osd, const osd_options &options) override { return 0; } void input_init(running_machine &machine) override { } - void poll_if_necessary(running_machine &machine) override { } - void pause() override { } - void resume() override { } + void poll_if_necessary(bool relative_reset) override { } }; -MODULE_DEFINITION(LIGHTGUN_NONE, lightgun_input_none) -class joystick_input_none : public input_module +class joystick_input_none : public osd_module, public input_module { public: - joystick_input_none() : input_module(OSD_JOYSTICKINPUT_PROVIDER, "none") { } - int init(const osd_options &options) override { return 0; } + joystick_input_none() : osd_module(OSD_JOYSTICKINPUT_PROVIDER, "none") { } + int init(osd_interface &osd, const osd_options &options) override { return 0; } void input_init(running_machine &machine) override { } - void poll_if_necessary(running_machine &machine) override { } - void pause() override { } - void resume() override { } + void poll_if_necessary(bool relative_reset) override { } }; -MODULE_DEFINITION(JOYSTICK_NONE, joystick_input_none) +} // anonymous namesapce + +} // namespace osd + + + +MODULE_DEFINITION(KEYBOARD_NONE, osd::keyboard_input_none) +MODULE_DEFINITION(MOUSE_NONE, osd::mouse_input_none) +MODULE_DEFINITION(LIGHTGUN_NONE, osd::lightgun_input_none) +MODULE_DEFINITION(JOYSTICK_NONE, osd::joystick_input_none) diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp index 94018f9c404..d6b99649c4a 100644 --- a/src/osd/modules/input/input_rawinput.cpp +++ b/src/osd/modules/input/input_rawinput.cpp @@ -10,27 +10,34 @@ #if defined(OSD_WINDOWS) -// MAME headers -#include "emu.h" - #include "input_windows.h" +#include "input_wincommon.h" + #include "winmain.h" #include "window.h" #include "modules/lib/osdlib.h" #include "strconv.h" +// MAME headers +#include "inpttype.h" + #include <algorithm> +#include <cassert> +#include <chrono> #include <functional> #include <mutex> #include <new> +#include <utility> // standard windows headers #include <windows.h> #include <tchar.h> +namespace osd { + namespace { class safe_regkey @@ -222,6 +229,7 @@ std::wstring improve_name_from_usb_path(const std::wstring ®path) return trim_prefix(regstring); } + //============================================================ // rawinput_device_improve_name //============================================================ @@ -262,19 +270,36 @@ std::wstring rawinput_device_improve_name(const std::wstring &name) class rawinput_device : public event_based_device<RAWINPUT> { -private: - HANDLE m_handle = nullptr; - public: - rawinput_device(running_machine &machine, std::string &&name, std::string &&id, input_device_class deviceclass, input_module &module) : - event_based_device(machine, std::move(name), std::move(id), deviceclass, module) + rawinput_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) : + event_based_device(std::move(name), std::move(id), module), + m_handle(handle) { } HANDLE device_handle() const { return m_handle; } - void set_handle(HANDLE handle) { m_handle = handle; } + + bool reconnect_candidate(std::string_view i) const { return !m_handle && (id() == i); } + + void detach_device() + { + assert(m_handle); + m_handle = nullptr; + osd_printf_verbose("RawInput: %s [ID %s] disconnected\n", name(), id()); + } + + void attach_device(HANDLE handle) + { + assert(!m_handle); + m_handle = handle; + osd_printf_verbose("RawInput: %s [ID %s] reconnected\n", name(), id()); + } + +private: + HANDLE m_handle; }; + //============================================================ // rawinput_keyboard_device //============================================================ @@ -282,172 +307,329 @@ public: class rawinput_keyboard_device : public rawinput_device { public: - keyboard_state keyboard; + 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 } }) + { + } - rawinput_keyboard_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - rawinput_device(machine, std::move(name), std::move(id), DEVICE_CLASS_KEYBOARD, module), - keyboard({ { 0 } }) + virtual void reset() override { + m_pause_pressed = std::chrono::steady_clock::time_point::min(); + memset(&m_keyboard, 0, sizeof(m_keyboard)); + m_e1 = 0xffff; } - void reset() override + virtual void poll(bool relative_reset) override { - memset(&keyboard, 0, sizeof(keyboard)); + 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; } - void process_event(RAWINPUT &rawinput) override + 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 - keyboard.state[scancode] = (rawinput.data.keyboard.Flags & RI_KEY_BREAK) ? 0x00 : 0x80; + m_keyboard.state[scancode] = (rawinput.data.keyboard.Flags & RI_KEY_BREAK) ? 0x00 : 0x80; } + + virtual void configure(input_device &device) override + { + keyboard_trans_table const &table = keyboard_trans_table::instance(); + + // 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 + // 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); + + // add the item to the device + device.add_item( + name, + util::string_format("SCAN%03d", keynum), + itemid, + generic_button_get_state<std::uint8_t>, + &m_keyboard.state[keynum]); + } + } + +private: + std::chrono::steady_clock::time_point m_pause_pressed; + uint16_t m_e1; + keyboard_state m_keyboard; }; + //============================================================ // rawinput_mouse_device //============================================================ class rawinput_mouse_device : public rawinput_device { -private: - std::mutex m_device_lock; public: - mouse_state mouse; - - rawinput_mouse_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - rawinput_device(machine, std::move(name), std::move(id), DEVICE_CLASS_MOUSE, module), - mouse({0}) + rawinput_mouse_device(std::string &&name, std::string &&id, input_module &module, HANDLE handle) : + rawinput_device(std::move(name), std::move(id), module, handle), + m_mouse({0}), + m_x(0), + m_y(0), + m_z(0) { } - void poll() override + virtual void poll(bool relative_reset) override { - mouse.lX = 0; - mouse.lY = 0; - mouse.lZ = 0; + rawinput_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.lZ = std::exchange(m_z, 0); + } + } - rawinput_device::poll(); + virtual void reset() override + { + memset(&m_mouse, 0, sizeof(m_mouse)); + m_x = m_y = m_z = 0; } - void reset() override + virtual void configure(input_device &device) override { - memset(&mouse, 0, sizeof(mouse)); + // 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); + } + + // populate the buttons + for (int butnum = 0; butnum < 5; butnum++) + { + device.add_item( + default_button_name(butnum), + std::string_view(), + input_item_id(ITEM_ID_BUTTON1 + butnum), + generic_button_get_state<BYTE>, + &m_mouse.rgbButtons[butnum]); + } } - void process_event(RAWINPUT &rawinput) override + 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) { - mouse.lX += rawinput.data.mouse.lLastX * osd::INPUT_RELATIVE_PER_PIXEL; - mouse.lY += rawinput.data.mouse.lLastY * osd::INPUT_RELATIVE_PER_PIXEL; + m_x += rawinput.data.mouse.lLastX * input_device::RELATIVE_PER_PIXEL; + m_y += rawinput.data.mouse.lLastY * input_device::RELATIVE_PER_PIXEL; - // update zaxis + // update Z axis (vertical scroll) if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) - mouse.lZ += static_cast<int16_t>(rawinput.data.mouse.usButtonData) * osd::INPUT_RELATIVE_PER_PIXEL; + m_z += 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) mouse.rgbButtons[0] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) mouse.rgbButtons[0] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) mouse.rgbButtons[1] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) mouse.rgbButtons[1] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) mouse.rgbButtons[2] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) mouse.rgbButtons[2] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) mouse.rgbButtons[3] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) mouse.rgbButtons[3] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) mouse.rgbButtons[4] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) mouse.rgbButtons[4] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_mouse.rgbButtons[0] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) m_mouse.rgbButtons[0] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) m_mouse.rgbButtons[1] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) m_mouse.rgbButtons[1] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) m_mouse.rgbButtons[2] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) m_mouse.rgbButtons[2] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) m_mouse.rgbButtons[3] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) m_mouse.rgbButtons[3] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) m_mouse.rgbButtons[4] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) m_mouse.rgbButtons[4] = 0x00; } } + +private: + mouse_state m_mouse; + LONG m_x, m_y, m_z; }; + //============================================================ // rawinput_lightgun_device //============================================================ class rawinput_lightgun_device : public rawinput_device { -private: - std::mutex m_device_lock; public: - mouse_state lightgun; - - rawinput_lightgun_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - rawinput_device(machine, std::move(name), std::move(id), DEVICE_CLASS_LIGHTGUN, module), - lightgun({0}) + 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) { } - void poll() override + virtual void poll(bool relative_reset) override { - lightgun.lZ = 0; + rawinput_device::poll(relative_reset); + if (relative_reset) + m_lightgun.lZ = std::exchange(m_z, 0); + } - rawinput_device::poll(); + virtual void reset() override + { + memset(&m_lightgun, 0, sizeof(m_lightgun)); + m_z = 0; } - void reset() override + virtual void configure(input_device &device) override { - memset(&lightgun, 0, sizeof(lightgun)); + // 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_lightgun.lX + axisnum); + } + + // scroll wheel is always relative if present + device.add_item( + default_axis_name[2], + std::string_view(), + ITEM_ID_ADD_RELATIVE1, + generic_axis_get_state<LONG>, + &m_lightgun.lZ); + + // populate the buttons + for (int butnum = 0; butnum < 5; butnum++) + { + device.add_item( + default_button_name(butnum), + std::string_view(), + input_item_id(ITEM_ID_BUTTON1 + butnum), + generic_button_get_state<BYTE>, + &m_lightgun.rgbButtons[butnum]); + } } - void process_event(RAWINPUT &rawinput) override + virtual void process_event(RAWINPUT const &rawinput) override { // If this data was intended for a rawinput lightgun if (rawinput.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { // update the X/Y positions - lightgun.lX = normalize_absolute_axis(rawinput.data.mouse.lLastX, 0, osd::INPUT_ABSOLUTE_MAX); - lightgun.lY = normalize_absolute_axis(rawinput.data.mouse.lLastY, 0, osd::INPUT_ABSOLUTE_MAX); + 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 if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_WHEEL) - lightgun.lZ += static_cast<int16_t>(rawinput.data.mouse.usButtonData) * osd::INPUT_RELATIVE_PER_PIXEL; + m_z += 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) lightgun.rgbButtons[0] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) lightgun.rgbButtons[0] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) lightgun.rgbButtons[1] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) lightgun.rgbButtons[1] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) lightgun.rgbButtons[2] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) lightgun.rgbButtons[2] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) lightgun.rgbButtons[3] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) lightgun.rgbButtons[3] = 0x00; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) lightgun.rgbButtons[4] = 0x80; - if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) lightgun.rgbButtons[4] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) m_lightgun.rgbButtons[0] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_UP) m_lightgun.rgbButtons[0] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) m_lightgun.rgbButtons[1] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_UP) m_lightgun.rgbButtons[1] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) m_lightgun.rgbButtons[2] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_UP) m_lightgun.rgbButtons[2] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) m_lightgun.rgbButtons[3] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) m_lightgun.rgbButtons[3] = 0x00; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) m_lightgun.rgbButtons[4] = 0x80; + if (rawinput.data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) m_lightgun.rgbButtons[4] = 0x00; } } + +private: + mouse_state m_lightgun; + LONG m_z; }; + //============================================================ -// rawinput_module - base class for rawinput modules +// rawinput_module - base class for RawInput modules //============================================================ -class rawinput_module : public wininput_module +class rawinput_module : public wininput_module<rawinput_device> { private: std::mutex m_module_lock; public: - rawinput_module(const char *type, const char *name) : wininput_module(type, name) + rawinput_module(const char *type, const char *name) : wininput_module<rawinput_device>(type, name) { } - bool probe() override + virtual bool probe() override { return true; } - void input_init(running_machine &machine) override + virtual void input_init(running_machine &machine) override { + wininput_module<rawinput_device>::input_init(machine); + // get initial number of devices UINT device_count = 0; if (GetRawInputDeviceList(nullptr, &device_count, sizeof(RAWINPUTDEVICELIST)) != 0) @@ -479,11 +661,7 @@ public: // iterate backwards through devices; new devices are added at the head for (int devnum = retrieved - 1; devnum >= 0; devnum--) - add_rawinput_device(machine, rawinput_devices[devnum]); - - // don't enable global inputs when debugging - if (!machine.options().debug()) - m_global_inputs_enabled = downcast<windows_options &>(machine.options()).global_inputs(); + add_rawinput_device(rawinput_devices[devnum]); // If we added no devices, no need to register for notifications if (devicelist().empty()) @@ -494,26 +672,21 @@ public: registration.usUsagePage = usagepage(); registration.usUsage = usage(); registration.dwFlags = RIDEV_DEVNOTIFY; - if (m_global_inputs_enabled) + if (background_input()) registration.dwFlags |= RIDEV_INPUTSINK; - registration.hwndTarget = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(); + registration.hwndTarget = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); // register the device RegisterRawInputDevices(®istration, 1, sizeof(registration)); } protected: - virtual void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST const &device) = 0; + virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) = 0; virtual USHORT usagepage() = 0; virtual USHORT usage() = 0; - int init_internal() override - { - return 0; - } - template<class TDevice> - TDevice *create_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST const &rawinputdevice) + TDevice *create_rawinput_device(input_device_class deviceclass, RAWINPUTDEVICELIST const &rawinputdevice) { // determine the length of the device name, allocate it, and fetch it if not nameless UINT name_length = 0; @@ -528,38 +701,34 @@ protected: if (wcsstr(tname.get(), L"Root#RDP_") != nullptr) return nullptr; - // improve the name and then allocate a device - std::string utf8_name = osd::text::from_wstring(rawinput_device_improve_name(tname.get())); + // improve the name + std::string utf8_name = text::from_wstring(rawinput_device_improve_name(tname.get())); // set device ID to raw input name - std::string utf8_id = osd::text::from_wstring(tname.get()); + std::string utf8_id = text::from_wstring(tname.get()); tname.reset(); - TDevice &devinfo = devicelist().create_device<TDevice>(machine, std::move(utf8_name), std::move(utf8_id), *this); - - // Add the handle - devinfo.set_handle(rawinputdevice.hDevice); - - return &devinfo; + // allocate a device + return &create_device<TDevice>( + deviceclass, + std::move(utf8_name), + std::move(utf8_id), + rawinputdevice.hDevice); } - bool handle_input_event(input_event eventid, void *eventdata) override + virtual bool handle_input_event(input_event eventid, void *eventdata) override { switch (eventid) { // handle raw input data case INPUT_EVENT_RAWINPUT: { - // ignore if not enabled - if (!input_enabled()) - return false; + HRAWINPUT const rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); - HRAWINPUT rawinputdevice = *static_cast<HRAWINPUT *>(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 @@ -569,7 +738,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; @@ -595,13 +764,12 @@ protected: devicelist().end(), [input] (auto const &device) { - auto devinfo = dynamic_cast<rawinput_device *>(device.get()); - return devinfo && (input->header.hDevice == devinfo->device_handle()); + return input->header.hDevice == device->device_handle(); }); if (devicelist().end() == target_device) return false; - static_cast<rawinput_device *>(target_device->get())->queue_events(input, 1); + (*target_device)->queue_events(input, 1); return true; } } @@ -609,7 +777,7 @@ protected: case INPUT_EVENT_ARRIVAL: { - HRAWINPUT rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); + HRAWINPUT const rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); // determine the length of the device name, allocate it, and fetch it if not nameless UINT name_length = 0; @@ -619,7 +787,7 @@ protected: std::unique_ptr<WCHAR []> tname = std::make_unique<WCHAR []>(name_length + 1); if (name_length > 1 && GetRawInputDeviceInfoW(rawinputdevice, RIDI_DEVICENAME, tname.get(), &name_length) == UINT(-1)) return false; - std::string utf8_id = osd::text::from_wstring(tname.get()); + std::string utf8_id = text::from_wstring(tname.get()); tname.reset(); std::lock_guard<std::mutex> scope_lock(m_module_lock); @@ -630,20 +798,19 @@ protected: devicelist().end(), [&utf8_id] (auto const &device) { - auto devinfo = dynamic_cast<rawinput_device *>(device.get()); - return devinfo && !devinfo->device_handle() && (devinfo->id() == utf8_id); + return device->reconnect_candidate(utf8_id); }); if (devicelist().end() == target_device) return false; - static_cast<rawinput_device *>(target_device->get())->set_handle(rawinputdevice); + (*target_device)->attach_device(rawinputdevice); return true; } break; case INPUT_EVENT_REMOVAL: { - HRAWINPUT rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); + HRAWINPUT const rawinputdevice = *static_cast<HRAWINPUT *>(eventdata); std::lock_guard<std::mutex> scope_lock(m_module_lock); @@ -653,15 +820,14 @@ protected: devicelist().end(), [rawinputdevice] (auto const &device) { - auto devinfo = dynamic_cast<rawinput_device *>(device.get()); - return devinfo && (rawinputdevice == devinfo->device_handle()); + return rawinputdevice == device->device_handle(); }); if (devicelist().end() == target_device) return false; (*target_device)->reset(); - static_cast<rawinput_device *>(target_device->get())->set_handle(nullptr); + (*target_device)->detach_device(); return true; } break; @@ -675,163 +841,109 @@ protected: } }; + //============================================================ -// keyboard_input_rawinput - rawinput keyboard module +// keyboard_input_rawinput - RawInput keyboard module //============================================================ class keyboard_input_rawinput : public rawinput_module { public: - keyboard_input_rawinput() - : rawinput_module(OSD_KEYBOARDINPUT_PROVIDER, "rawinput") + keyboard_input_rawinput() : rawinput_module(OSD_KEYBOARDINPUT_PROVIDER, "rawinput") { } protected: - USHORT usagepage() override { return 1; } - USHORT usage() override { return 6; } + virtual USHORT usagepage() override { return 1; } + virtual USHORT usage() override { return 6; } - void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST const &device) override + virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override { // make sure this is a keyboard if (device.dwType != RIM_TYPEKEYBOARD) return; // allocate and link in a new device - auto *devinfo = create_rawinput_device<rawinput_keyboard_device>(machine, device); - if (devinfo == nullptr) - return; - - keyboard_trans_table &table = keyboard_trans_table::instance(); - - // populate it - for (int 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) - _snwprintf(keyname, std::size(keyname), L"Scan%03d", keynum); - std::string name = osd::text::from_wstring(keyname); - - // add the item to the device - devinfo->device()->add_item(name, itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]); - } + create_rawinput_device<rawinput_keyboard_device>(DEVICE_CLASS_KEYBOARD, device); } }; + //============================================================ -// mouse_input_rawinput - rawinput mouse module +// mouse_input_rawinput - RawInput mouse module //============================================================ class mouse_input_rawinput : public rawinput_module { public: - mouse_input_rawinput() - : rawinput_module(OSD_MOUSEINPUT_PROVIDER, "rawinput") + mouse_input_rawinput() : rawinput_module(OSD_MOUSEINPUT_PROVIDER, "rawinput") { } protected: - USHORT usagepage() override { return 1; } - USHORT usage() override { return 2; } + virtual USHORT usagepage() override { return 1; } + virtual USHORT usage() override { return 2; } - void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST const &device) override + virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override { // make sure this is a mouse if (device.dwType != RIM_TYPEMOUSE) return; // allocate and link in a new device - auto *devinfo = create_rawinput_device<rawinput_mouse_device>(machine, device); - if (devinfo == nullptr) - return; - - // populate the axes - for (int axisnum = 0; axisnum < 3; axisnum++) - { - devinfo->device()->add_item( - default_axis_name[axisnum], - static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &devinfo->mouse.lX + axisnum); - } - - // populate the buttons - for (int butnum = 0; butnum < 5; butnum++) - { - devinfo->device()->add_item( - default_button_name(butnum), - static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state<BYTE>, - &devinfo->mouse.rgbButtons[butnum]); - } + create_rawinput_device<rawinput_mouse_device>(DEVICE_CLASS_MOUSE, device); } }; + //============================================================ -// lightgun_input_rawinput - rawinput lightgun module +// lightgun_input_rawinput - RawInput lightgun module //============================================================ class lightgun_input_rawinput : public rawinput_module { public: - lightgun_input_rawinput() - : rawinput_module(OSD_LIGHTGUNINPUT_PROVIDER, "rawinput") + lightgun_input_rawinput() : rawinput_module(OSD_LIGHTGUNINPUT_PROVIDER, "rawinput") { } protected: - USHORT usagepage() override { return 1; } - USHORT usage() override { return 2; } + virtual USHORT usagepage() override { return 1; } + virtual USHORT usage() override { return 2; } - void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST const &device) override + virtual void add_rawinput_device(RAWINPUTDEVICELIST const &device) override { - // make sure this is a mouse if (device.dwType != RIM_TYPEMOUSE) return; // allocate and link in a new device - auto *devinfo = create_rawinput_device<rawinput_lightgun_device>(machine, device); - if (devinfo == nullptr) - return; - - // populate the axes - for (int axisnum = 0; axisnum < 3; axisnum++) - { - devinfo->device()->add_item( - default_axis_name[axisnum], - static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &devinfo->lightgun.lX + axisnum); - } - - // populate the buttons - for (int butnum = 0; butnum < 5; butnum++) - { - devinfo->device()->add_item( - default_button_name(butnum), - static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state<BYTE>, - &devinfo->lightgun.rgbButtons[butnum]); - } + create_rawinput_device<rawinput_lightgun_device>(DEVICE_CLASS_LIGHTGUN, device); } }; } // anonymous namespace +} // namespace osd + #else // defined(OSD_WINDOWS) #include "input_module.h" +namespace osd { + +namespace { + MODULE_NOT_SUPPORTED(keyboard_input_rawinput, OSD_KEYBOARDINPUT_PROVIDER, "rawinput") MODULE_NOT_SUPPORTED(mouse_input_rawinput, OSD_MOUSEINPUT_PROVIDER, "rawinput") MODULE_NOT_SUPPORTED(lightgun_input_rawinput, OSD_LIGHTGUNINPUT_PROVIDER, "rawinput") +} // anonymous namespace + +} // namespace osd + #endif // defined(OSD_WINDOWS) -MODULE_DEFINITION(KEYBOARDINPUT_RAWINPUT, keyboard_input_rawinput) -MODULE_DEFINITION(MOUSEINPUT_RAWINPUT, mouse_input_rawinput) -MODULE_DEFINITION(LIGHTGUNINPUT_RAWINPUT, lightgun_input_rawinput) +MODULE_DEFINITION(KEYBOARDINPUT_RAWINPUT, osd::keyboard_input_rawinput) +MODULE_DEFINITION(MOUSEINPUT_RAWINPUT, osd::mouse_input_rawinput) +MODULE_DEFINITION(LIGHTGUNINPUT_RAWINPUT, osd::lightgun_input_rawinput) diff --git a/src/osd/modules/input/input_sdl.cpp b/src/osd/modules/input/input_sdl.cpp index a3bbe494194..cc7cb2f1451 100644 --- a/src/osd/modules/input/input_sdl.cpp +++ b/src/osd/modules/input/input_sdl.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes +// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes, Vas Crabb //============================================================ // // input_sdl.cpp - SDL 2.0 implementation of MAME input routines @@ -12,34 +12,38 @@ //============================================================ #include "input_module.h" + #include "modules/osdmodule.h" -#if defined(SDLMAME_SDL2) +#if defined(OSD_SDL) + +#include "assignmenthelper.h" +#include "input_common.h" + +#include "interface/inputseq.h" +#include "modules/lib/osdobj_common.h" +#include "sdl/osdsdl.h" + +// emu +#include "inpttype.h" -// standard sdl header +// standard SDL header #include <SDL2/SDL.h> -#include <cctype> -// ReSharper disable once CppUnusedIncludeDirective + #include <algorithm> +#include <cctype> +#include <chrono> #include <cstddef> +#include <cstring> +#include <initializer_list> #include <iterator> +#include <list> #include <memory> -#include <mutex> #include <optional> -#include <queue> - -// MAME headers -#include "emu.h" -#include "uiinput.h" -#include "strconv.h" -#include "unicode.h" - -// MAMEOS headers -#include "input_common.h" -#include "../lib/osdobj_common.h" -#include "input_sdlcommon.h" -#include "../../sdl/osdsdl.h" -#include "../../sdl/window.h" +#include <string> +#include <string_view> +#include <tuple> +#include <utility> // winnt.h defines this #ifdef DELETE @@ -47,11 +51,217 @@ #endif +namespace osd { + namespace { -// FIXME: sdl does not properly report the window for certain OS. -#define GET_FOCUS_WINDOW(ev) focus_window() -//#define GET_FOCUS_WINDOW(ev) window_from_id((ev)->windowID) +char const *const CONTROLLER_AXIS_XBOX[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "LT", + "RT" }; + +char const *const CONTROLLER_AXIS_PS[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "L2", + "R2" }; + +char const *const CONTROLLER_AXIS_SWITCH[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "ZL", + "ZR" }; + +char const *const CONTROLLER_BUTTON_XBOX360[]{ + "A", + "B", + "X", + "Y", + "Back", + "Guide", + "Start", + "LSB", + "RSB", + "LB", + "RB", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Share", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +char const *const CONTROLLER_BUTTON_XBOXONE[]{ + "A", + "B", + "X", + "Y", + "View", + "Logo", + "Menu", + "LSB", + "RSB", + "LB", + "RB", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Share", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +char const *const CONTROLLER_BUTTON_PS3[]{ + "Cross", + "Circle", + "Square", + "Triangle", + "Select", + "PS", + "Start", + "L3", + "R3", + "L1", + "R1", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Mute", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +char const *const CONTROLLER_BUTTON_PS4[]{ + "Cross", + "Circle", + "Square", + "Triangle", + "Share", + "PS", + "Options", + "L3", + "R3", + "L1", + "R1", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Mute", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +char const *const CONTROLLER_BUTTON_PS5[]{ + "Cross", + "Circle", + "Square", + "Triangle", + "Create", + "PS", + "Options", + "L3", + "R3", + "L1", + "R1", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Mute", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +char const *const CONTROLLER_BUTTON_SWITCH[]{ + "A", + "B", + "X", + "Y", + "-", + "Home", + "+", + "LSB", + "RSB", + "L", + "R", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Capture", + "RSR", + "LSL", + "RSL", + "LSR", + "Touchpad" }; + +[[maybe_unused]] char const *const CONTROLLER_BUTTON_STADIA[]{ + "A", + "B", + "X", + "Y", + "Options", + "Logo", + "Menu", + "L3", + "R3", + "L1", + "R1", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Capture", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; + +[[maybe_unused]] char const *const CONTROLLER_BUTTON_SHIELD[]{ + "A", + "B", + "X", + "Y", + "Back", + "Logo", + "Start", + "LSB", + "RSB", + "LB", + "RB", + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "Share", + "P1", + "P2", + "P3", + "P4", + "Touchpad" }; struct key_lookup_table { @@ -60,12 +270,8 @@ struct key_lookup_table }; #define KE(x) { SDL_SCANCODE_ ## x, "SDL_SCANCODE_" #x }, -#define KE8(A, B, C, D, E, F, G, H) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G) KE(H) -#define KE7(A, B, C, D, E, F, G) KE(A) KE(B) KE(C) KE(D) KE(E) KE(F) KE(G) -#define KE5(A, B, C, D, E) KE(A) KE(B) KE(C) KE(D) KE(E) -#define KE3(A, B, C) KE(A) KE(B) KE(C) -key_lookup_table sdl_lookup_table[] = +key_lookup_table const sdl_lookup_table[] = { KE(UNKNOWN) @@ -324,48 +530,29 @@ key_lookup_table sdl_lookup_table[] = KE(APP1) KE(APP2) - - -{ - -1, "" -} }; + //============================================================ // lookup_sdl_code //============================================================ -int lookup_sdl_code(const char *scode) +int lookup_sdl_code(std::string_view scode) { - int i = 0; - - while (sdl_lookup_table[i].code >= 0) - { - if (!strcmp(scode, sdl_lookup_table[i].name)) - return sdl_lookup_table[i].code; - i++; - } - return -1; + auto const found = std::find_if( + std::begin(sdl_lookup_table), + std::end(sdl_lookup_table), + [&scode] (auto const &key) { return scode == key.name; }); + return (std::end(sdl_lookup_table) != found) ? found->code : -1; } + //============================================================ // sdl_device //============================================================ -class sdl_device : public event_based_device<SDL_Event> -{ -public: - sdl_device(running_machine &machine, std::string &&name, std::string &&id, input_device_class devclass, input_module &module) : - event_based_device(machine, std::move(name), std::move(id), devclass, module) - { - } +using sdl_device = event_based_device<SDL_Event>; -protected: - std::shared_ptr<sdl_window_info> focus_window() - { - return sdl_event_manager::instance().focus_window(); - } -}; //============================================================ // sdl_keyboard_device @@ -374,506 +561,1454 @@ protected: class sdl_keyboard_device : public sdl_device { public: - // state information for a keyboard - struct keyboard_state + sdl_keyboard_device( + std::string &&name, + std::string &&id, + input_module &module, + keyboard_trans_table const &trans_table) : + sdl_device(std::move(name), std::move(id), module), + m_trans_table(trans_table), + m_keyboard({{0}}), + m_capslock_pressed(std::chrono::steady_clock::time_point::min()) { - int32_t state[0x3ff]; // must be int32_t! - int8_t oldkey[MAX_KEYS]; - int8_t currkey[MAX_KEYS]; - }; - - keyboard_state keyboard; - - sdl_keyboard_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - sdl_device(machine, std::move(name), std::move(id), DEVICE_CLASS_KEYBOARD, module), - keyboard({{0}}) - { - #ifdef __APPLE__ - m_capslock_hack = false; - machine.add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&sdl_keyboard_device::frame_callback, this)); - #endif } - #ifdef __APPLE__ - unsigned m_capslock_hack; - void frame_callback() + virtual void poll(bool relative_reset) override { - if (m_capslock_hack) - if (--m_capslock_hack == 0) - keyboard.state[SDL_SCANCODE_CAPSLOCK] = 0x00; + sdl_device::poll(relative_reset); + +#ifdef __APPLE__ + if (m_keyboard.state[SDL_SCANCODE_CAPSLOCK] && (std::chrono::steady_clock::now() > (m_capslock_pressed + std::chrono::milliseconds(30)))) + m_keyboard.state[SDL_SCANCODE_CAPSLOCK] = 0x00; +#endif } - #endif - void process_event(SDL_Event &sdlevent) override + virtual void process_event(SDL_Event const &event) override { - switch (sdlevent.type) + switch (event.type) { case SDL_KEYDOWN: + if (event.key.keysym.scancode == SDL_SCANCODE_CAPSLOCK) + m_capslock_pressed = std::chrono::steady_clock::now(); - #ifdef __APPLE__ - if (sdlevent.key.keysym.scancode == SDL_SCANCODE_CAPSLOCK) - m_capslock_hack = 2; - #endif - - keyboard.state[sdlevent.key.keysym.scancode] = 0x80; - if (sdlevent.key.keysym.sym < 0x20) - machine().ui_input().push_char_event(osd_common_t::s_window_list.front()->target(), sdlevent.key.keysym.sym); - else if (keyboard.state[SDL_SCANCODE_LCTRL] == 0x80 || keyboard.state[SDL_SCANCODE_RCTRL] == 0x80) - { - // SDL filters out control characters for text input, so they are decoded here - if (sdlevent.key.keysym.sym >= 0x40 && sdlevent.key.keysym.sym < 0x7f) - machine().ui_input().push_char_event(osd_common_t::s_window_list.front()->target(), sdlevent.key.keysym.sym & 0x1f); - else if (keyboard.state[SDL_SCANCODE_LSHIFT] == 0x80 || keyboard.state[SDL_SCANCODE_RSHIFT] == 0x80) - { - if (sdlevent.key.keysym.sym == SDLK_6) // Ctrl-^ (RS) - machine().ui_input().push_char_event(osd_common_t::s_window_list.front()->target(), 0x1e); - else if (sdlevent.key.keysym.sym == SDLK_MINUS) // Ctrl-_ (US) - machine().ui_input().push_char_event(osd_common_t::s_window_list.front()->target(), 0x1f); - } - } + m_keyboard.state[event.key.keysym.scancode] = 0x80; break; case SDL_KEYUP: - #ifdef __APPLE__ - if (sdlevent.key.keysym.scancode == SDL_SCANCODE_CAPSLOCK) +#ifdef __APPLE__ + if (event.key.keysym.scancode == SDL_SCANCODE_CAPSLOCK) break; - #endif - - keyboard.state[sdlevent.key.keysym.scancode] = 0x00; - break; +#endif - case SDL_TEXTINPUT: - if (*sdlevent.text.text) - { - auto window = GET_FOCUS_WINDOW(&event.text); - //printf("Focus window is %p - wl %p\n", window, osd_common_t::s_window_list); - if (window != nullptr) - { - auto ptr = sdlevent.text.text; - auto len = std::strlen(sdlevent.text.text); - while (len) - { - char32_t ch; - auto chlen = uchar_from_utf8(&ch, ptr, len); - if (0 > chlen) - { - ch = 0x0fffd; - chlen = 1; - } - ptr += chlen; - len -= chlen; - machine().ui_input().push_char_event(window->target(), ch); - } - } - } + m_keyboard.state[event.key.keysym.scancode] = 0x00; break; } } - void reset() override + virtual void reset() override { - memset(&keyboard.state, 0, sizeof(keyboard.state)); - #ifdef __APPLE__ - m_capslock_hack = 0; - #endif + memset(&m_keyboard.state, 0, sizeof(m_keyboard.state)); + m_capslock_pressed = std::chrono::steady_clock::time_point::min(); } + + virtual void configure(input_device &device) override + { + // populate it + for (int keynum = 0; m_trans_table[keynum].mame_key != ITEM_ID_INVALID; keynum++) + { + input_item_id itemid = m_trans_table[keynum].mame_key; + device.add_item( + m_trans_table[keynum].ui_name, + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_keyboard.state[m_trans_table[keynum].sdl_scancode]); + } + } + +private: + // state information for a keyboard + struct keyboard_state + { + s32 state[0x3ff]; // must be s32! + s8 oldkey[MAX_KEYS]; + s8 currkey[MAX_KEYS]; + }; + + keyboard_trans_table const &m_trans_table; + keyboard_state m_keyboard; + std::chrono::steady_clock::time_point m_capslock_pressed; }; + //============================================================ // sdl_mouse_device //============================================================ class sdl_mouse_device : public sdl_device { -private: - const std::chrono::milliseconds double_click_speed = std::chrono::milliseconds(250); - std::chrono::system_clock::time_point last_click; - int last_x; - int last_y; - public: - // state information for a mouse - struct mouse_state + 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) { - int32_t lX, lY; - int32_t buttons[MAX_BUTTONS]; - }; - - mouse_state mouse; + } - sdl_mouse_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - sdl_device(machine, std::move(name), std::move(id), DEVICE_CLASS_MOUSE, module), - last_x(0), - last_y(0), - mouse({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); + } } - void reset() override + virtual void reset() override { - memset(&mouse, 0, sizeof(mouse)); + memset(&m_mouse, 0, sizeof(m_mouse)); + m_x = m_y = m_v = m_h = 0; } - void poll() override + virtual void configure(input_device &device) override { - mouse.lX = 0; - mouse.lY = 0; - sdl_device::poll(); + // add the axes + device.add_item( + "X", + std::string_view(), + ITEM_ID_XAXIS, + generic_axis_get_state<s32>, + &m_mouse.lX); + device.add_item( + "Y", + std::string_view(), + ITEM_ID_YAXIS, + generic_axis_get_state<s32>, + &m_mouse.lY); + device.add_item( + "Scroll V", + std::string_view(), + ITEM_ID_ZAXIS, + generic_axis_get_state<s32>, + &m_mouse.lV); + device.add_item( + "Scroll H", + std::string_view(), + ITEM_ID_RZAXIS, + generic_axis_get_state<s32>, + &m_mouse.lH); + + // add the buttons + for (int button = 0; button < 4; 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]); + } } - virtual void process_event(SDL_Event &sdlevent) override + virtual void process_event(SDL_Event const &event) override { - switch (sdlevent.type) + switch (event.type) { case SDL_MOUSEMOTION: - mouse.lX += sdlevent.motion.xrel * osd::INPUT_RELATIVE_PER_PIXEL; - mouse.lY += sdlevent.motion.yrel * osd::INPUT_RELATIVE_PER_PIXEL; + m_x += event.motion.xrel * input_device::RELATIVE_PER_PIXEL; + m_y += event.motion.yrel * input_device::RELATIVE_PER_PIXEL; + break; - { - int cx = -1, cy = -1; - auto window = GET_FOCUS_WINDOW(&sdlevent.motion); + case SDL_MOUSEBUTTONDOWN: + m_mouse.buttons[event.button.button - 1] = 0x80; + break; - if (window != nullptr && window->xy_to_render_target(sdlevent.motion.x, sdlevent.motion.y, &cx, &cy)) - machine().ui_input().push_mouse_move_event(window->target(), cx, cy); - } + case SDL_MOUSEBUTTONUP: + m_mouse.buttons[event.button.button - 1] = 0; break; - case SDL_MOUSEBUTTONDOWN: - mouse.buttons[sdlevent.button.button - 1] = 0x80; - //printf("But down %d %d %d %d %s\n", event.button.which, event.button.button, event.button.x, event.button.y, devinfo->name.c_str()); - if (sdlevent.button.button == 1) - { - int cx, cy; - auto click = std::chrono::system_clock::now(); - auto window = GET_FOCUS_WINDOW(&sdlevent.button); - if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) - { - machine().ui_input().push_mouse_down_event(window->target(), cx, cy); + case SDL_MOUSEWHEEL: +#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; +#else + m_v += event.wheel.y * input_device::RELATIVE_PER_PIXEL; + m_h += event.wheel.x * input_device::RELATIVE_PER_PIXEL; +#endif + break; + } + } - // avoid overflow with std::chrono::time_point::min() by adding rather than subtracting - if (click < last_click + double_click_speed - && (cx >= last_x - 4 && cx <= last_x + 4) - && (cy >= last_y - 4 && cy <= last_y + 4)) - { - last_click = std::chrono::time_point<std::chrono::system_clock>::min(); - machine().ui_input().push_mouse_double_click_event(window->target(), cx, cy); - } - else - { - last_click = click; - last_x = cx; - last_y = cy; - } - } - } +private: + // state information for a mouse + struct mouse_state + { + s32 lX, lY, lV, lH; + s32 buttons[MAX_BUTTONS]; + }; - else if (sdlevent.button.button == 3) - { - int cx, cy; - auto window = GET_FOCUS_WINDOW(&sdlevent.button); + mouse_state m_mouse; + s32 m_x, m_y, m_v, m_h; +}; - if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) - { - machine().ui_input().push_mouse_rdown_event(window->target(), cx, cy); - } - } - break; - case SDL_MOUSEBUTTONUP: - mouse.buttons[sdlevent.button.button - 1] = 0; - //printf("But up %d %d %d %d\n", event.button.which, event.button.button, event.button.x, event.button.y); +//============================================================ +// sdl_joystick_device_base +//============================================================ - if (sdlevent.button.button == 1) - { - int cx, cy; - auto window = GET_FOCUS_WINDOW(&sdlevent.button); +class sdl_joystick_device_base : public sdl_device, protected joystick_assignment_helper +{ +public: + std::optional<std::string> const &serial() const { return m_serial; } + SDL_JoystickID instance() const { return m_instance; } - if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) - { - machine().ui_input().push_mouse_up_event(window->target(), cx, cy); - } - } - else if (sdlevent.button.button == 3) - { - int cx, cy; - auto window = GET_FOCUS_WINDOW(&sdlevent.button); + bool is_instance(SDL_JoystickID instance) const { return m_instance == instance; } - if (window != nullptr && window->xy_to_render_target(sdlevent.button.x, sdlevent.button.y, &cx, &cy)) - { - machine().ui_input().push_mouse_rup_event(window->target(), cx, cy); - } - } - break; + bool reconnect_match(std::string_view g, char const *s) const + { + return + (0 > m_instance) && + (id() == g) && + ((s && serial() && (*serial() == s)) || (!s && !serial())); + } - case SDL_MOUSEWHEEL: - auto window = GET_FOCUS_WINDOW(&sdlevent.wheel); - if (window != nullptr) - machine().ui_input().push_mouse_wheel_event(window->target(), 0, 0, sdlevent.wheel.y, 3); - break; - } +protected: + sdl_joystick_device_base( + std::string &&name, + std::string &&id, + input_module &module, + char const *serial) : + sdl_device(std::move(name), std::move(id), module), + m_instance(-1) + { + if (serial) + m_serial = serial; } + + void set_instance(SDL_JoystickID instance) + { + assert(0 > m_instance); + assert(0 <= instance); + + m_instance = instance; + } + + void clear_instance() + { + m_instance = -1; + } + +private: + std::optional<std::string> m_serial; + SDL_JoystickID m_instance; }; + //============================================================ // sdl_joystick_device //============================================================ -class sdl_joystick_device : public sdl_device +class sdl_joystick_device : public sdl_joystick_device_base { public: - // state information for a joystick - struct sdl_joystick_state + sdl_joystick_device( + std::string &&name, + std::string &&id, + input_module &module, + SDL_Joystick *joy, + char const *serial) : + sdl_joystick_device_base( + std::move(name), + std::move(id), + module, + serial), + m_joystick({{0}}), + m_joydevice(joy), + m_hapdevice(SDL_HapticOpenFromJoystick(joy)) { - int32_t axes[MAX_AXES]; - int32_t buttons[MAX_BUTTONS]; - int32_t hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS]; - int32_t balls[MAX_AXES]; - }; + set_instance(SDL_JoystickInstanceID(joy)); + } - struct sdl_api_state + virtual void configure(input_device &device) override { - SDL_Joystick *device = nullptr; - SDL_Haptic *hapdevice = nullptr; - SDL_JoystickID joystick_id; - std::optional<std::string> serial; - }; + input_device::assignment_vector assignments; + char tempname[32]; - sdl_joystick_state joystick; - sdl_api_state sdl_state; + int const axiscount = SDL_JoystickNumAxes(m_joydevice); + int const buttoncount = SDL_JoystickNumButtons(m_joydevice); + int const hatcount = SDL_JoystickNumHats(m_joydevice); + int const ballcount = SDL_JoystickNumBalls(m_joydevice); - sdl_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - sdl_device(machine, std::move(name), std::move(id), DEVICE_CLASS_JOYSTICK, module), - joystick({{0}}) - { + // loop over all axes + input_item_id axisactual[MAX_AXES]; + for (int axis = 0; (axis < MAX_AXES) && (axis < axiscount); axis++) + { + input_item_id itemid; + + if (axis < INPUT_MAX_AXIS) + itemid = input_item_id(ITEM_ID_XAXIS + axis); + else if (axis < (INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE)) + itemid = input_item_id(ITEM_ID_ADD_ABSOLUTE1 + axis - INPUT_MAX_AXIS); + else + itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE; + + snprintf(tempname, sizeof(tempname), "A%d", axis + 1); + axisactual[axis] = device.add_item( + tempname, + std::string_view(), + itemid, + generic_axis_get_state<s32>, + &m_joystick.axes[axis]); + } + + // loop over all buttons + for (int button = 0; (button < MAX_BUTTONS) && (button < buttoncount); button++) + { + input_item_id itemid; + + m_joystick.buttons[button] = 0; + + if (button < INPUT_MAX_BUTTONS) + itemid = input_item_id(ITEM_ID_BUTTON1 + button); + else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) + itemid = input_item_id(ITEM_ID_ADD_SWITCH1 + button - INPUT_MAX_BUTTONS); + else + itemid = ITEM_ID_OTHER_SWITCH; + + input_item_id const actual = device.add_item( + default_button_name(button), + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_joystick.buttons[button]); + + // there are sixteen action button types + if (button < 16) + { + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual)); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button), SEQ_TYPE_STANDARD, seq); + + // assign the first few buttons to UI actions and pedals + switch (button) + { + case 0: + assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq); + break; + case 1: + assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back((3 > buttoncount) ? IPT_UI_CLEAR : IPT_UI_BACK, SEQ_TYPE_STANDARD, seq); + break; + case 2: + assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq); + assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq); + break; + case 3: + assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq); + break; + } + } + } + + // loop over all hats + input_item_id hatactual[MAX_HATS][4]; + for (int hat = 0; (hat < MAX_HATS) && (hat < hatcount); hat++) + { + input_item_id itemid; + + snprintf(tempname, sizeof(tempname), "Hat %d Up", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + (4 * hat) : ITEM_ID_OTHER_SWITCH); + hatactual[hat][0] = device.add_item( + tempname, + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_joystick.hatsU[hat]); + + snprintf(tempname, sizeof(tempname), "Hat %d Down", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + (4 * hat) : ITEM_ID_OTHER_SWITCH); + hatactual[hat][1] = device.add_item( + tempname, + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_joystick.hatsD[hat]); + + snprintf(tempname, sizeof(tempname), "Hat %d Left", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + (4 * hat) : ITEM_ID_OTHER_SWITCH); + hatactual[hat][2] = device.add_item( + tempname, + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_joystick.hatsL[hat]); + + snprintf(tempname, sizeof(tempname), "Hat %d Right", hat + 1); + itemid = input_item_id((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + (4 * hat) : ITEM_ID_OTHER_SWITCH); + hatactual[hat][3] = device.add_item( + tempname, + std::string_view(), + itemid, + generic_button_get_state<s32>, + &m_joystick.hatsR[hat]); + } + + // loop over all (track)balls + for (int ball = 0; (ball < (MAX_AXES / 2)) && (ball < ballcount); ball++) + { + int itemid; + + if (ball * 2 < INPUT_MAX_ADD_RELATIVE) + itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2; + else + itemid = ITEM_ID_OTHER_AXIS_RELATIVE; + + snprintf(tempname, sizeof(tempname), "R%d X", ball + 1); + input_item_id const xactual = device.add_item( + tempname, + std::string_view(), + input_item_id(itemid), + generic_axis_get_state<s32>, + &m_joystick.balls[ball * 2]); + + snprintf(tempname, sizeof(tempname), "R%d Y", ball + 1); + input_item_id const yactual = device.add_item( + tempname, + std::string_view(), + input_item_id(itemid + 1), + generic_axis_get_state<s32>, + &m_joystick.balls[ball * 2 + 1]); + + if (0 == ball) + { + // assign the first trackball to dial, trackball, mouse and lightgun inputs + input_seq const xseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, xactual)); + input_seq const yseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, yactual)); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, yseq); + assignments.emplace_back(IPT_MOUSE_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_MOUSE_Y, SEQ_TYPE_STANDARD, yseq); + if (2 > axiscount) + { + // use it for joystick inputs if axes are limited + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, xseq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, yseq); + } + else + { + // use for non-centring throttle control + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, yseq); + } + } + else if ((1 == ball) && (2 > axiscount)) + { + // provide a non-centring throttle control + input_seq const yseq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, yactual)); + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, yseq); + } + } + + // set up default assignments for axes and hats + add_directional_assignments( + assignments, + (1 <= axiscount) ? axisactual[0] : ITEM_ID_INVALID, // assume first axis is X + (2 <= axiscount) ? axisactual[1] : ITEM_ID_INVALID, // assume second axis is Y + (1 <= hatcount) ? hatactual[0][2] : ITEM_ID_INVALID, + (1 <= hatcount) ? hatactual[0][3] : ITEM_ID_INVALID, + (1 <= hatcount) ? hatactual[0][0] : ITEM_ID_INVALID, + (1 <= hatcount) ? hatactual[0][1] : ITEM_ID_INVALID); + if (2 <= axiscount) + { + // put pedals on the last of the second, third or fourth axis + input_item_id const pedalitem = axisactual[(std::min)(axiscount, 4) - 1]; + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitem))); + assignments.emplace_back( + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, pedalitem))); + } + if (3 <= axiscount) + { + // assign X/Y to one of the twin sticks + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_LEFT : IPT_JOYSTICKRIGHT_LEFT, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_LEFT, axisactual[0]))); + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_RIGHT : IPT_JOYSTICKRIGHT_RIGHT, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_RIGHT, axisactual[0]))); + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_UP : IPT_JOYSTICKRIGHT_UP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_UP, axisactual[1]))); + assignments.emplace_back( + (4 <= axiscount) ? IPT_JOYSTICKLEFT_DOWN : IPT_JOYSTICKRIGHT_DOWN, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_DOWN, axisactual[1]))); + + // use third or fourth axis for Z + input_seq const seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axisactual[(std::min)(axiscount, 4) - 1])); + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, seq); + + // use this for focus next/previous to make system selection menu practical to navigate + input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisactual[2])); + input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisactual[2])); + assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, downseq); + if (4 <= axiscount) + { + // use for zoom as well if there's another axis to use for previous/next group + assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, downseq); + assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, upseq); + } + + // use this for twin sticks, too + assignments.emplace_back((4 <= axiscount) ? IPT_JOYSTICKRIGHT_LEFT : IPT_JOYSTICKLEFT_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back((4 <= axiscount) ? IPT_JOYSTICKRIGHT_RIGHT : IPT_JOYSTICKLEFT_DOWN, SEQ_TYPE_STANDARD, downseq); + + // put previous/next group on the last of the third or fourth axis + input_item_id const groupitem = axisactual[(std::min)(axiscount, 4) - 1]; + assignments.emplace_back( + IPT_UI_PREV_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, groupitem))); + assignments.emplace_back( + IPT_UI_NEXT_GROUP, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, groupitem))); + } + if (4 <= axiscount) + { + // use this for twin sticks + input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisactual[3])); + input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisactual[3])); + assignments.emplace_back(IPT_JOYSTICKRIGHT_UP, SEQ_TYPE_STANDARD, upseq); + assignments.emplace_back(IPT_JOYSTICKRIGHT_DOWN, SEQ_TYPE_STANDARD, downseq); + } + + // set default assignments + device.set_default_assignments(std::move(assignments)); } ~sdl_joystick_device() { - if (sdl_state.device) + if (m_joydevice) { - if (sdl_state.hapdevice) + if (m_hapdevice) { - SDL_HapticClose(sdl_state.hapdevice); - sdl_state.hapdevice = nullptr; + SDL_HapticClose(m_hapdevice); + m_hapdevice = nullptr; } - SDL_JoystickClose(sdl_state.device); - sdl_state.device = nullptr; + SDL_JoystickClose(m_joydevice); + m_joydevice = nullptr; } } - void reset() override + virtual void reset() override { - memset(&joystick, 0, sizeof(joystick)); + memset(&m_joystick, 0, sizeof(m_joystick)); } - void process_event(SDL_Event &sdlevent) override + virtual void process_event(SDL_Event const &event) override { - switch (sdlevent.type) + switch (event.type) { case SDL_JOYAXISMOTION: - joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2); + if (event.jaxis.axis < MAX_AXES) + m_joystick.axes[event.jaxis.axis] = (event.jaxis.value * 2); break; case SDL_JOYBALLMOTION: - //printf("Ball %d %d\n", sdlevent.jball.xrel, sdlevent.jball.yrel); - joystick.balls[sdlevent.jball.ball * 2] = sdlevent.jball.xrel * osd::INPUT_RELATIVE_PER_PIXEL; - joystick.balls[sdlevent.jball.ball * 2 + 1] = sdlevent.jball.yrel * osd::INPUT_RELATIVE_PER_PIXEL; + //printf("Ball %d %d\n", event.jball.xrel, event.jball.yrel); + if (event.jball.ball < (MAX_AXES / 2)) + { + m_joystick.balls[event.jball.ball * 2] = event.jball.xrel * input_device::RELATIVE_PER_PIXEL; + m_joystick.balls[event.jball.ball * 2 + 1] = event.jball.yrel * input_device::RELATIVE_PER_PIXEL; + } break; case SDL_JOYHATMOTION: - joystick.hatsU[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_UP) ? 0x80 : 0; - joystick.hatsD[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_DOWN) ? 0x80 : 0; - joystick.hatsL[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_LEFT) ? 0x80 : 0; - joystick.hatsR[sdlevent.jhat.hat] = (sdlevent.jhat.value & SDL_HAT_RIGHT) ? 0x80 : 0; + if (event.jhat.hat < MAX_HATS) + { + m_joystick.hatsU[event.jhat.hat] = (event.jhat.value & SDL_HAT_UP) ? 0x80 : 0; + m_joystick.hatsD[event.jhat.hat] = (event.jhat.value & SDL_HAT_DOWN) ? 0x80 : 0; + m_joystick.hatsL[event.jhat.hat] = (event.jhat.value & SDL_HAT_LEFT) ? 0x80 : 0; + m_joystick.hatsR[event.jhat.hat] = (event.jhat.value & SDL_HAT_RIGHT) ? 0x80 : 0; + } break; case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: - if (sdlevent.jbutton.button < MAX_BUTTONS) - joystick.buttons[sdlevent.jbutton.button] = (sdlevent.jbutton.state == SDL_PRESSED) ? 0x80 : 0; + if (event.jbutton.button < MAX_BUTTONS) + m_joystick.buttons[event.jbutton.button] = (event.jbutton.state == SDL_PRESSED) ? 0x80 : 0; break; case SDL_JOYDEVICEREMOVED: - osd_printf_verbose("Joystick: %s [GUID %s] disconnected\n", name(), id()); + osd_printf_verbose("Joystick: %s [ID %s] disconnected\n", name(), id()); + clear_instance(); reset(); - if (sdl_state.device) + if (m_joydevice) { - if (sdl_state.hapdevice) + if (m_hapdevice) { - SDL_HapticClose(sdl_state.hapdevice); - sdl_state.hapdevice = nullptr; + SDL_HapticClose(m_hapdevice); + m_hapdevice = nullptr; } - SDL_JoystickClose(sdl_state.device); - sdl_state.device = nullptr; + SDL_JoystickClose(m_joydevice); + m_joydevice = nullptr; } break; } } + + bool has_haptic() const + { + return m_hapdevice != nullptr; + } + + void attach_device(SDL_Joystick *joy) + { + assert(joy); + assert(!m_joydevice); + + set_instance(SDL_JoystickInstanceID(joy)); + m_joydevice = joy; + m_hapdevice = SDL_HapticOpenFromJoystick(joy); + + osd_printf_verbose("Joystick: %s [ID %s] reconnected\n", name(), id()); + } + +protected: + // state information for a joystick + struct sdl_joystick_state + { + s32 axes[MAX_AXES]; + s32 buttons[MAX_BUTTONS]; + s32 hatsU[MAX_HATS], hatsD[MAX_HATS], hatsL[MAX_HATS], hatsR[MAX_HATS]; + s32 balls[MAX_AXES]; + }; + + sdl_joystick_state m_joystick; + +private: + SDL_Joystick *m_joydevice; + SDL_Haptic *m_hapdevice; }; + +//============================================================ +// sdl_sixaxis_joystick_device +//============================================================ + class sdl_sixaxis_joystick_device : public sdl_joystick_device { public: - sdl_sixaxis_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - sdl_joystick_device(machine, std::move(name), std::move(id), module) - { - } + using sdl_joystick_device::sdl_joystick_device; - void process_event(SDL_Event &sdlevent) override + virtual void process_event(SDL_Event const &event) override { - switch (sdlevent.type) + switch (event.type) { case SDL_JOYAXISMOTION: { - int const axis = sdlevent.jaxis.axis; + int const axis = event.jaxis.axis; if (axis <= 3) { - joystick.axes[sdlevent.jaxis.axis] = (sdlevent.jaxis.value * 2); + m_joystick.axes[event.jaxis.axis] = (event.jaxis.value * 2); } else { - int const magic = (sdlevent.jaxis.value / 2) + 16384; - joystick.axes[sdlevent.jaxis.axis] = magic; + int const magic = (event.jaxis.value / 2) + 16384; + m_joystick.axes[event.jaxis.axis] = magic; } } break; default: // Call the base for other events - sdl_joystick_device::process_event(sdlevent); + sdl_joystick_device::process_event(event); break; } } }; + //============================================================ -// sdl_input_module +// sdl_game_controller_device //============================================================ -class sdl_input_module : public input_module_base, public sdl_event_subscriber +class sdl_game_controller_device : public sdl_joystick_device_base { public: - sdl_input_module(const char *type) : input_module_base(type, "sdl") + sdl_game_controller_device( + std::string &&name, + std::string &&id, + input_module &module, + SDL_GameController *ctrl, + char const *serial) : + sdl_joystick_device_base( + std::move(name), + std::move(id), + module, + serial), + m_controller({{0}}), + m_ctrldevice(ctrl) { + set_instance(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl))); } - void input_init(running_machine &machine) override + ~sdl_game_controller_device() { - if (machine.debug_flags & DEBUG_FLAG_OSD_ENABLED) + if (m_ctrldevice) { - osd_printf_warning("Debug Build: Disabling input grab for -debug\n"); - set_mouse_enabled(false); + SDL_GameControllerClose(m_ctrldevice); + m_ctrldevice = nullptr; } } - void exit() override + virtual void configure(input_device &device) override { - // unsubscribe for events - sdl_event_manager::instance().unsubscribe(this); + input_device::assignment_vector assignments; + char const *const *axisnames = CONTROLLER_AXIS_XBOX; + char const *const *buttonnames = CONTROLLER_BUTTON_XBOX360; + bool digitaltriggers = false; + bool avoidpaddles = false; + auto const ctrltype = SDL_GameControllerGetType(m_ctrldevice); + switch (ctrltype) + { + case SDL_CONTROLLER_TYPE_UNKNOWN: + osd_printf_verbose("Game Controller: ... unknown type\n", int(ctrltype)); + break; + case SDL_CONTROLLER_TYPE_XBOX360: + osd_printf_verbose("Game Controller: ... Xbox 360 type\n"); + axisnames = CONTROLLER_AXIS_XBOX; + buttonnames = CONTROLLER_BUTTON_XBOX360; + break; + case SDL_CONTROLLER_TYPE_XBOXONE: + osd_printf_verbose("Game Controller: ... Xbox One type\n"); + axisnames = CONTROLLER_AXIS_XBOX; + buttonnames = CONTROLLER_BUTTON_XBOXONE; + break; + case SDL_CONTROLLER_TYPE_PS3: + osd_printf_verbose("Game Controller: ... PlayStation 3 type\n"); + axisnames = CONTROLLER_AXIS_PS; + buttonnames = CONTROLLER_BUTTON_PS3; + break; + case SDL_CONTROLLER_TYPE_PS4: + osd_printf_verbose("Game Controller: ... PlayStation 4 type\n"); + axisnames = CONTROLLER_AXIS_PS; + buttonnames = CONTROLLER_BUTTON_PS4; + break; + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: + osd_printf_verbose("Game Controller: ... Switch Pro Controller type\n"); + axisnames = CONTROLLER_AXIS_SWITCH; + buttonnames = CONTROLLER_BUTTON_SWITCH; + digitaltriggers = true; + break; + //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; +#if SDL_VERSION_ATLEAST(2, 0, 16) + //case SDL_CONTROLLER_TYPE_AMAZON_LUNA: + case SDL_CONTROLLER_TYPE_GOOGLE_STADIA: + osd_printf_verbose("Game Controller: ... Google Stadia type\n"); + axisnames = CONTROLLER_AXIS_PS; + buttonnames = CONTROLLER_BUTTON_STADIA; + break; +#endif +#if SDL_VERSION_ATLEAST(2, 24, 0) + case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD: + osd_printf_verbose("Game Controller: ... NVIDIA Shield type\n"); + axisnames = CONTROLLER_AXIS_XBOX; + buttonnames = CONTROLLER_BUTTON_SHIELD; + break; + //case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: + //case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: + case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: + osd_printf_verbose("Game Controller: ... Joy-Con pair type\n"); + axisnames = CONTROLLER_AXIS_SWITCH; + buttonnames = CONTROLLER_BUTTON_SWITCH; + digitaltriggers = true; + avoidpaddles = true; + break; +#endif + default: // default to Xbox 360 names + osd_printf_verbose("Game Controller: ... unrecognized type (%d)\n", int(ctrltype)); + break; + } + + // keep track of item numbers as we add controls + std::pair<input_item_id, input_item_id> axisitems[SDL_CONTROLLER_AXIS_MAX]; + input_item_id buttonitems[SDL_CONTROLLER_BUTTON_MAX]; + std::tuple<input_item_id, SDL_GameControllerButton, SDL_GameControllerAxis> numberedbuttons[16]; + std::fill( + std::begin(axisitems), + std::end(axisitems), + std::make_pair(ITEM_ID_INVALID, ITEM_ID_INVALID)); + std::fill( + std::begin(buttonitems), + std::end(buttonitems), + ITEM_ID_INVALID); + std::fill( + std::begin(numberedbuttons), + std::end(numberedbuttons), + std::make_tuple(ITEM_ID_INVALID, SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_INVALID)); + + // add axes + std::tuple<SDL_GameControllerAxis, input_item_id, bool> const axes[]{ + { SDL_CONTROLLER_AXIS_LEFTX, ITEM_ID_XAXIS, false }, + { SDL_CONTROLLER_AXIS_LEFTY, ITEM_ID_YAXIS, false }, + { SDL_CONTROLLER_AXIS_RIGHTX, ITEM_ID_ZAXIS, false }, + { SDL_CONTROLLER_AXIS_RIGHTY, ITEM_ID_RZAXIS, false }, + { SDL_CONTROLLER_AXIS_TRIGGERLEFT, ITEM_ID_SLIDER1, true }, + { SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ITEM_ID_SLIDER2, true } }; + for (auto [axis, item, buttontest] : axes) + { + bool avail = !buttontest || !digitaltriggers; + avail = avail && SDL_GameControllerHasAxis(m_ctrldevice, axis); + if (avail) + { + auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + case SDL_CONTROLLER_BINDTYPE_BUTTON: + if (buttontest) + avail = false; + break; + default: + break; + } + } + if (avail) + { + axisitems[axis].first = device.add_item( + axisnames[axis], + std::string_view(), + item, + generic_axis_get_state<s32>, + &m_controller.axes[axis]); + } + } + + // add automatically numbered buttons + std::tuple<SDL_GameControllerButton, SDL_GameControllerAxis, bool> const generalbuttons[]{ + { SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_AXIS_INVALID, true }, + { SDL_CONTROLLER_BUTTON_INVALID, SDL_CONTROLLER_AXIS_TRIGGERLEFT, true }, + { 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 }, + { 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 }, + { SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_AXIS_INVALID, false }, + { SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_AXIS_INVALID, false }, + { SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_AXIS_INVALID, false }, + }; + input_item_id button_item = ITEM_ID_BUTTON1; + unsigned buttoncount = 0; + for (auto [button, axis, field] : generalbuttons) + { + bool avail = true; + input_item_id actual = ITEM_ID_INVALID; + if (SDL_CONTROLLER_BUTTON_INVALID != button) + { + avail = SDL_GameControllerHasButton(m_ctrldevice, button); + if (avail) + { + auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + default: + break; + } + } + if (avail) + { + actual = buttonitems[button] = device.add_item( + buttonnames[button], + std::string_view(), + button_item++, + generic_button_get_state<s32>, + &m_controller.buttons[button]); + if (field && (std::size(numberedbuttons) > buttoncount)) + std::get<1>(numberedbuttons[buttoncount]) = button; + } + } + else + { + avail = SDL_GameControllerHasAxis(m_ctrldevice, axis); + if (avail) + { + auto const binding = SDL_GameControllerGetBindForAxis(m_ctrldevice, axis); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + case SDL_CONTROLLER_BINDTYPE_BUTTON: + break; + default: + avail = digitaltriggers; + } + } + if (avail) + { + actual = axisitems[axis].second = device.add_item( + axisnames[axis], + std::string_view(), + button_item++, + [] (void *device_internal, void *item_internal) -> int + { + return (*reinterpret_cast<s32 const *>(item_internal) <= -16'384) ? 1 : 0; + }, + &m_controller.axes[axis]); + if (field && (std::size(numberedbuttons) > buttoncount)) + std::get<2>(numberedbuttons[buttoncount]) = axis; + } + } + + // add default button assignments + if (field && avail && (std::size(numberedbuttons) > buttoncount)) + { + std::get<0>(numberedbuttons[buttoncount]) = actual; + add_button_assignment(assignments, ioport_type(IPT_BUTTON1 + buttoncount++), { actual }); + } + } + + // add buttons with fixed item IDs + std::pair<SDL_GameControllerButton, input_item_id> const fixedbuttons[]{ + { SDL_CONTROLLER_BUTTON_BACK, ITEM_ID_SELECT }, + { SDL_CONTROLLER_BUTTON_START, ITEM_ID_START }, + { SDL_CONTROLLER_BUTTON_DPAD_UP, ITEM_ID_HAT1UP }, + { SDL_CONTROLLER_BUTTON_DPAD_DOWN, ITEM_ID_HAT1DOWN }, + { SDL_CONTROLLER_BUTTON_DPAD_LEFT, ITEM_ID_HAT1LEFT }, + { SDL_CONTROLLER_BUTTON_DPAD_RIGHT, ITEM_ID_HAT1RIGHT } }; + for (auto [button, item] : fixedbuttons) + { + bool avail = true; + avail = SDL_GameControllerHasButton(m_ctrldevice, button); + if (avail) + { + auto const binding = SDL_GameControllerGetBindForButton(m_ctrldevice, button); + switch (binding.bindType) + { + case SDL_CONTROLLER_BINDTYPE_NONE: + avail = false; + break; + default: + break; + } + } + if (avail) + { + buttonitems[button] = device.add_item( + buttonnames[button], + std::string_view(), + item, + generic_button_get_state<s32>, + &m_controller.buttons[button]); + } + } + + // try to get a "complete" joystick for primary movement controls + input_item_id diraxis[2][2]; + choose_primary_stick( + diraxis, + axisitems[SDL_CONTROLLER_AXIS_LEFTX].first, + axisitems[SDL_CONTROLLER_AXIS_LEFTY].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first); + + // now set up controls using the primary joystick + add_directional_assignments( + assignments, + diraxis[0][0], + diraxis[0][1], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_LEFT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_RIGHT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_UP], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_DOWN]); + + // assign a secondary stick axis to joystick Z if available + bool const zaxis = add_assignment( + assignments, + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NONE, + { diraxis[1][1], diraxis[1][0] }); + if (!zaxis) + { + // if both triggers are present, combine them, or failing that, fall back to a pair of buttons + if ((ITEM_ID_INVALID != axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first) && (ITEM_ID_INVALID != axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first)) + { + assignments.emplace_back( + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + input_seq( + make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first), + make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_REVERSE, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first))); + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER])) + { + // took shoulder buttons + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second)) + { + // took trigger buttons + } + 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 + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4])) + { + // took P3/P4 + } + } + + // prefer trigger axes for pedals, otherwise take half axes and buttons + unsigned pedalbutton = 0; + if (!add_assignment(assignments, IPT_PEDAL, SEQ_TYPE_STANDARD, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, { axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].first })) + { + add_assignment( + assignments, + IPT_PEDAL, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NEG, + { diraxis[1][1], diraxis[0][1] }); + bool const incbutton = add_assignment( + assignments, + IPT_PEDAL, + SEQ_TYPE_INCREMENT, + ITEM_CLASS_SWITCH, + ITEM_MODIFIER_NONE, + { axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second, buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] }); + if (!incbutton) + { + if (add_assignment(assignments, IPT_PEDAL, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) })) + ++pedalbutton; + } + } + if (!add_assignment(assignments, IPT_PEDAL2, SEQ_TYPE_STANDARD, ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, { axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].first })) + { + add_assignment( + assignments, + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_POS, + { diraxis[1][1], diraxis[0][1] }); + bool const incbutton = add_assignment( + assignments, + IPT_PEDAL2, + SEQ_TYPE_INCREMENT, + ITEM_CLASS_SWITCH, + ITEM_MODIFIER_NONE, + { axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] }); + if (!incbutton) + { + if (add_assignment(assignments, IPT_PEDAL2, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) })) + ++pedalbutton; + } + } + add_assignment(assignments, IPT_PEDAL3, SEQ_TYPE_INCREMENT, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, { std::get<0>(numberedbuttons[pedalbutton]) }); + + // potentially use thumb sticks and/or D-pad and A/B/X/Y diamond for twin sticks + add_twin_stick_assignments( + assignments, + axisitems[SDL_CONTROLLER_AXIS_LEFTX].first, + axisitems[SDL_CONTROLLER_AXIS_LEFTY].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first, + axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first, + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_LEFT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_RIGHT], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_UP], + buttonitems[SDL_CONTROLLER_BUTTON_DPAD_DOWN], + buttonitems[SDL_CONTROLLER_BUTTON_X], + buttonitems[SDL_CONTROLLER_BUTTON_B], + buttonitems[SDL_CONTROLLER_BUTTON_Y], + buttonitems[SDL_CONTROLLER_BUTTON_A]); + + // add assignments for buttons with fixed functions + add_button_assignment(assignments, IPT_SELECT, { buttonitems[SDL_CONTROLLER_BUTTON_BACK] }); + add_button_assignment(assignments, IPT_START, { buttonitems[SDL_CONTROLLER_BUTTON_START] }); + add_button_assignment(assignments, IPT_UI_MENU, { buttonitems[SDL_CONTROLLER_BUTTON_GUIDE] }); + + // the first button is always UI select + if (add_button_assignment(assignments, IPT_UI_SELECT, { std::get<0>(numberedbuttons[0]) })) + { + if (SDL_CONTROLLER_BUTTON_INVALID != std::get<1>(numberedbuttons[0])) + buttonitems[std::get<1>(numberedbuttons[0])] = ITEM_ID_INVALID; + if (SDL_CONTROLLER_AXIS_INVALID != std::get<2>(numberedbuttons[0])) + axisitems[std::get<2>(numberedbuttons[0])].second = ITEM_ID_INVALID; + } + + // try to get a matching pair of buttons for previous/next group + if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axisitems[SDL_CONTROLLER_AXIS_TRIGGERLEFT].second, axisitems[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].second)) + { + // took digital triggers + } + 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 + } + 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 + } + 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 + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1])) + { + // took secondary Y + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][0])) + { + // took secondary X + } + + // try to get a matching pair of buttons for page up/down + 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 + } + else if (!avoidpaddles && consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, buttonitems[SDL_CONTROLLER_BUTTON_PADDLE3], buttonitems[SDL_CONTROLLER_BUTTON_PADDLE4])) + { + // took lower paddles + } + else + 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 + } + else if (consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, diraxis[1][1])) + { + // took secondary Y + } - input_module_base::exit(); + // try to assign X button to UI clear + if (add_button_assignment(assignments, IPT_UI_CLEAR, { buttonitems[SDL_CONTROLLER_BUTTON_X] })) + { + buttonitems[SDL_CONTROLLER_BUTTON_X] = ITEM_ID_INVALID; + } + else + { + // otherwise try to find an unassigned button + for (auto [item, button, axis] : numberedbuttons) + { + if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button])) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + buttonitems[button] = ITEM_ID_INVALID; + break; + } + else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second)) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + axisitems[axis].second = ITEM_ID_INVALID; + break; + } + } + } + + // try to assign B button to UI back + if (add_button_assignment(assignments, IPT_UI_BACK, { buttonitems[SDL_CONTROLLER_BUTTON_B] })) + { + buttonitems[SDL_CONTROLLER_BUTTON_X] = ITEM_ID_INVALID; + } + else + { + // otherwise try to find an unassigned button + for (auto [item, button, axis] : numberedbuttons) + { + if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button])) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + buttonitems[button] = ITEM_ID_INVALID; + break; + } + else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second)) + { + add_button_assignment(assignments, IPT_UI_CLEAR, { item }); + axisitems[axis].second = ITEM_ID_INVALID; + break; + } + } + } + + // try to assign Y button to UI help + if (add_button_assignment(assignments, IPT_UI_HELP, { buttonitems[SDL_CONTROLLER_BUTTON_Y] })) + { + buttonitems[SDL_CONTROLLER_BUTTON_Y] = ITEM_ID_INVALID; + } + else + { + // otherwise try to find an unassigned button + for (auto [item, button, axis] : numberedbuttons) + { + if ((SDL_CONTROLLER_BUTTON_INVALID != button) && (ITEM_ID_INVALID != buttonitems[button])) + { + add_button_assignment(assignments, IPT_UI_HELP, { item }); + buttonitems[button] = ITEM_ID_INVALID; + break; + } + else if ((SDL_CONTROLLER_AXIS_INVALID != axis) && (ITEM_ID_INVALID != axisitems[axis].second)) + { + add_button_assignment(assignments, IPT_UI_HELP, { item }); + axisitems[axis].second = ITEM_ID_INVALID; + break; + } + } + } + + // put focus previous/next on the shoulder buttons if available - this can be overloaded with zoom + if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER])) + { + // took shoulder buttons + } + else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][0])) + { + // took secondary X + } + else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][1])) + { + // took secondary Y + } + + // put zoom on the secondary stick if available, or fall back to shoulder buttons + if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, diraxis[1][0])) + { + // took secondary X + if (axisitems[SDL_CONTROLLER_AXIS_LEFTX].first == diraxis[1][0]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_LEFTSTICK] }); + else if (axisitems[SDL_CONTROLLER_AXIS_RIGHTX].first == diraxis[1][0]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSTICK] }); + diraxis[1][0] = ITEM_ID_INVALID; + } + else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_IN, IPT_UI_ZOOM_OUT, diraxis[1][1])) + { + // took secondary Y + if (axisitems[SDL_CONTROLLER_AXIS_LEFTY].first == diraxis[1][1]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_LEFTSTICK] }); + else if (axisitems[SDL_CONTROLLER_AXIS_RIGHTY].first == diraxis[1][1]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSTICK] }); + diraxis[1][1] = ITEM_ID_INVALID; + } + else if (consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, buttonitems[SDL_CONTROLLER_BUTTON_LEFTSHOULDER], buttonitems[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER])) + { + // took shoulder buttons + } + + // set default assignments + device.set_default_assignments(std::move(assignments)); } - void before_poll(running_machine& machine) override + virtual void reset() override { - // Tell the event manager to process events and push them to the devices - sdl_event_manager::instance().process_events(machine); + memset(&m_controller, 0, sizeof(m_controller)); + } + + virtual void process_event(SDL_Event const &event) override + { + switch (event.type) + { + case SDL_CONTROLLERAXISMOTION: + if (event.caxis.axis < SDL_CONTROLLER_AXIS_MAX) + { + switch (event.caxis.axis) + { + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: // MAME wants negative values for triggers + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + m_controller.axes[event.caxis.axis] = -normalize_absolute_axis(event.caxis.value, -32'767, 32'767); + break; + default: + m_controller.axes[event.caxis.axis] = normalize_absolute_axis(event.caxis.value, -32'767, 32'767); + } + } + break; + + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + if (event.cbutton.button < SDL_CONTROLLER_BUTTON_MAX) + m_controller.buttons[event.cbutton.button] = (event.cbutton.state == SDL_PRESSED) ? 0x80 : 0x00; + break; + + 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; + } + break; + } } - bool should_poll_devices(running_machine& machine) override + void attach_device(SDL_GameController *ctrl) { - return sdl_event_manager::instance().has_focus() && input_enabled(); + assert(ctrl); + assert(!m_ctrldevice); + + set_instance(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ctrl))); + m_ctrldevice = ctrl; + + osd_printf_verbose("Game Controller: %s [ID %s] reconnected\n", name(), id()); } - void handle_event(SDL_Event &sdlevent) override +private: + // state information for a game controller + struct sdl_controller_state + { + s32 axes[SDL_CONTROLLER_AXIS_MAX]; + s32 buttons[SDL_CONTROLLER_BUTTON_MAX]; + }; + + sdl_controller_state m_controller; + SDL_GameController *m_ctrldevice; +}; + + +//============================================================ +// sdl_input_module +//============================================================ + +template <typename Info> +class sdl_input_module : + public input_module_impl<Info, sdl_osd_interface>, + protected sdl_event_manager::subscriber +{ +public: + sdl_input_module(char const *type, char const *name) : + input_module_impl<Info, sdl_osd_interface>(type, name) { - // By default dispatch event to every device - devicelist().for_each_device( - [&sdlevent](auto device) { - downcast<sdl_device*>(device)->queue_events(&sdlevent, 1); - }); + } + + virtual void exit() override + { + // unsubscribe for events + unsubscribe(); + + input_module_impl<Info, sdl_osd_interface>::exit(); + } + +protected: + virtual void handle_event(SDL_Event const &event) override + { + // dispatch event to every device by default + this->devicelist().for_each_device( + [&event] (auto &device) { device.queue_events(&event, 1); }); } }; + //============================================================ // sdl_keyboard_module //============================================================ -class sdl_keyboard_module : public sdl_input_module +class sdl_keyboard_module : public sdl_input_module<sdl_keyboard_device> { - keyboard_trans_table * m_key_trans_table; public: sdl_keyboard_module() : - sdl_input_module(OSD_KEYBOARDINPUT_PROVIDER), - m_key_trans_table(nullptr) + sdl_input_module<sdl_keyboard_device>(OSD_KEYBOARDINPUT_PROVIDER, "sdl") { } - void input_init(running_machine &machine) override + virtual void input_init(running_machine &machine) override { - sdl_input_module::input_init(machine); + sdl_input_module<sdl_keyboard_device>::input_init(machine); static int const event_types[] = { int(SDL_KEYDOWN), - int(SDL_KEYUP), - int(SDL_TEXTINPUT) }; + int(SDL_KEYUP) }; - sdl_event_manager::instance().subscribe(event_types, this); + subscribe(osd(), event_types); // Read our keymap and store a pointer to our table - m_key_trans_table = sdlinput_read_keymap(machine); - - keyboard_trans_table& local_table = *m_key_trans_table; + sdlinput_read_keymap(); osd_printf_verbose("Keyboard: Start initialization\n"); // SDL only has 1 keyboard add it now - auto &devinfo = devicelist().create_device<sdl_keyboard_device>(machine, "System keyboard", "System keyboard", *this); - - // populate it - for (int keynum = 0; local_table[keynum].mame_key != ITEM_ID_INVALID; keynum++) - { - input_item_id itemid = local_table[keynum].mame_key; - - // generate the default / modified name - char defname[20]; - snprintf(defname, sizeof(defname) - 1, "%s", local_table[keynum].ui_name); - - devinfo.device()->add_item(defname, itemid, generic_button_get_state<std::int32_t>, &devinfo.keyboard.state[local_table[keynum].sdl_scancode]); - } + auto &devinfo = create_device<sdl_keyboard_device>( + DEVICE_CLASS_KEYBOARD, + "System keyboard", + "System keyboard", + *m_key_trans_table); osd_printf_verbose("Keyboard: Registered %s\n", devinfo.name()); osd_printf_verbose("Keyboard: End initialization\n"); } private: - keyboard_trans_table *sdlinput_read_keymap(running_machine &machine) + void sdlinput_read_keymap() { keyboard_trans_table &default_table = keyboard_trans_table::instance(); - if (!machine.options().bool_value(SDLOPTION_KEYMAP)) - return &default_table; + // 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; - const char *const keymap_filename = downcast<sdl_options &>(machine.options()).keymap_file(); + 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); FILE *const keymap_file = fopen(keymap_filename, "r"); 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)) @@ -906,7 +2041,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); @@ -921,22 +2056,21 @@ private: } fclose(keymap_file); osd_printf_verbose("Keymap: Processed %d lines\n", line); - - return m_custom_table.get(); } - 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; }; + //============================================================ // sdl_mouse_module //============================================================ -class sdl_mouse_module : public sdl_input_module +class sdl_mouse_module : public sdl_input_module<sdl_mouse_device> { public: - sdl_mouse_module() : sdl_input_module(OSD_MOUSEINPUT_PROVIDER) + sdl_mouse_module() : sdl_input_module<sdl_mouse_device>(OSD_MOUSEINPUT_PROVIDER, "sdl") { } @@ -950,22 +2084,15 @@ public: int(SDL_MOUSEBUTTONUP), int(SDL_MOUSEWHEEL) }; - sdl_event_manager::instance().subscribe(event_types, this); + subscribe(osd(), event_types); osd_printf_verbose("Mouse: Start initialization\n"); // SDL currently only supports one mouse - auto &devinfo = devicelist().create_device<sdl_mouse_device>(machine, "System mouse", "System mouse", *this); - - // add the axes - devinfo.device()->add_item("X", ITEM_ID_XAXIS, generic_axis_get_state<std::int32_t>, &devinfo.mouse.lX); - devinfo.device()->add_item("Y", ITEM_ID_YAXIS, generic_axis_get_state<std::int32_t>, &devinfo.mouse.lY); - - for (int button = 0; button < 4; button++) - { - input_item_id itemid = (input_item_id)(ITEM_ID_BUTTON1 + button); - devinfo.device()->add_item(default_button_name(button), itemid, generic_button_get_state<std::int32_t>, &devinfo.mouse.buttons[button]); - } + auto &devinfo = create_device<sdl_mouse_device>( + DEVICE_CLASS_MOUSE, + "System mouse", + "System mouse"); osd_printf_verbose("Mouse: Registered %s\n", devinfo.name()); osd_printf_verbose("Mouse: End initialization\n"); @@ -974,181 +2101,310 @@ public: //============================================================ -// sdl_joystick_module +// sdl_joystick_module_base //============================================================ -class sdl_joystick_module : public sdl_input_module +class sdl_joystick_module_base : public sdl_input_module<sdl_joystick_device_base> { -private: - bool m_initialized_joystick; - bool m_initialized_haptic; - bool m_sixaxis_mode; -public: - sdl_joystick_module() : - sdl_input_module(OSD_JOYSTICKINPUT_PROVIDER), +protected: + sdl_joystick_module_base(char const *name) : + sdl_input_module<sdl_joystick_device_base>(OSD_JOYSTICKINPUT_PROVIDER, name), m_initialized_joystick(false), - m_initialized_haptic(false), - m_sixaxis_mode(false) + m_initialized_haptic(false) { } - virtual void exit() override + virtual ~sdl_joystick_module_base() + { + assert(!m_initialized_joystick); + assert(!m_initialized_haptic); + } + + bool have_joystick() const { return m_initialized_joystick; } + bool have_haptic() const { return m_initialized_haptic; } + + void init_joystick() { - sdl_input_module::exit(); + assert(!m_initialized_joystick); + assert(!m_initialized_haptic); + + m_initialized_joystick = !SDL_InitSubSystem(SDL_INIT_JOYSTICK); + if (!m_initialized_joystick) + { + osd_printf_error("Could not initialize SDL Joystick subsystem: %s.\n", SDL_GetError()); + return; + } + + m_initialized_haptic = !SDL_InitSubSystem(SDL_INIT_HAPTIC); + if (!m_initialized_haptic) + osd_printf_verbose("Could not initialize SDL Haptic subsystem: %s.\n", SDL_GetError()); + } + void quit_joystick() + { if (m_initialized_joystick) { SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + m_initialized_joystick = false; } if (m_initialized_haptic) { SDL_QuitSubSystem(SDL_INIT_HAPTIC); + m_initialized_haptic = false; } } - virtual void input_init(running_machine &machine) override + sdl_joystick_device *create_joystick_device(int index, bool sixaxis) { - SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); - - m_initialized_joystick = !SDL_InitSubSystem(SDL_INIT_JOYSTICK); - if (!m_initialized_joystick) + // open the joystick device + SDL_Joystick *const joy = SDL_JoystickOpen(index); + if (!joy) { - osd_printf_error("Could not initialize SDL Joystick: %s.\n", SDL_GetError()); - return; + osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", index, SDL_GetError()); + return nullptr; } - m_initialized_haptic = !SDL_InitSubSystem(SDL_INIT_HAPTIC); - if (!m_initialized_haptic) + // get basic info + char const *const name = SDL_JoystickName(joy); + SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); + char guid_str[256]; + guid_str[0] = '\0'; + SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); + char const *const serial = SDL_JoystickGetSerial(joy); + std::string id(guid_str); + if (serial) + id.append(1, '-').append(serial); + + // print some diagnostic info + osd_printf_verbose("Joystick: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n", + name ? name : "<nullptr>", + guid_str, + SDL_JoystickGetVendor(joy), + SDL_JoystickGetProduct(joy), + SDL_JoystickGetProductVersion(joy), + serial ? serial : "<nullptr>"); + osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n", + SDL_JoystickNumAxes(joy), + SDL_JoystickNumButtons(joy), + SDL_JoystickNumHats(joy), + SDL_JoystickNumBalls(joy)); + if (SDL_JoystickNumButtons(joy) > MAX_BUTTONS) + osd_printf_verbose("Joystick: ... Has %d buttons which exceeds supported %d buttons\n", SDL_JoystickNumButtons(joy), MAX_BUTTONS); + + // instantiate device + sdl_joystick_device &devinfo = sixaxis + ? create_device<sdl_sixaxis_joystick_device>(DEVICE_CLASS_JOYSTICK, name ? name : guid_str, guid_str, joy, serial) + : create_device<sdl_joystick_device>(DEVICE_CLASS_JOYSTICK, name ? name : guid_str, guid_str, joy, serial); + + if (devinfo.has_haptic()) + osd_printf_verbose("Joystick: ... Has haptic capability\n"); + else + osd_printf_verbose("Joystick: ... Does not have haptic capability\n"); + + return &devinfo; + } + + void dispatch_joystick_event(SDL_Event const &event) + { + // figure out which joystick this event is destined for + sdl_joystick_device_base *const target_device = find_joystick(event.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32 + + // if we find a matching joystick, dispatch the event to the joystick + if (target_device) + target_device->queue_events(&event, 1); + } + + device_info *find_reconnect_match(SDL_JoystickGUID const &guid, char const *serial) + { + char guid_str[256]; + guid_str[0] = '\0'; + SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); + auto target_device = std::find_if( + devicelist().begin(), + devicelist().end(), + [&guid_str, &serial] (auto const &device) + { + return device->reconnect_match(guid_str, serial); + }); + return (devicelist().end() != target_device) ? target_device->get() : nullptr; + } + + sdl_joystick_device_base *find_joystick(SDL_JoystickID instance) + { + for (auto &device : devicelist()) { - osd_printf_verbose("Could not initialize SDL Haptic subsystem: %s.\n", SDL_GetError()); + if (device->is_instance(instance)) + return device.get(); } + return nullptr; + } - sdl_input_module::input_init(machine); +private: + bool m_initialized_joystick; + bool m_initialized_haptic; +}; + + +//============================================================ +// sdl_joystick_module +//============================================================ + +class sdl_joystick_module : public sdl_joystick_module_base +{ +public: + sdl_joystick_module() : sdl_joystick_module_base("sdljoy") + { + } + + virtual void exit() override + { + sdl_joystick_module_base::exit(); - char tempname[512]; + quit_joystick(); + } - m_sixaxis_mode = downcast<const sdl_options *>(options())->sixaxis(); + virtual void input_init(running_machine &machine) override + { + auto &sdlopts = dynamic_cast<sdl_options const &>(*options()); + bool const sixaxis_mode = sdlopts.sixaxis(); + + if (!sdlopts.debug() && sdlopts.background_input()) + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + + init_joystick(); + if (!have_joystick()) + return; + + sdl_joystick_module_base::input_init(machine); osd_printf_verbose("Joystick: Start initialization\n"); for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) + create_joystick_device(physical_stick, sixaxis_mode); + + static int const event_types[] = { + int(SDL_JOYAXISMOTION), + int(SDL_JOYBALLMOTION), + int(SDL_JOYHATMOTION), + int(SDL_JOYBUTTONDOWN), + int(SDL_JOYBUTTONUP), + int(SDL_JOYDEVICEADDED), + int(SDL_JOYDEVICEREMOVED) }; + subscribe(osd(), event_types); + + osd_printf_verbose("Joystick: End initialization\n"); + } + + virtual void handle_event(SDL_Event const &event) override + { + if (SDL_JOYDEVICEADDED == event.type) { - SDL_Joystick *const joy = SDL_JoystickOpen(physical_stick); - char const *const name = SDL_JoystickName(joy); - SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); - char guid_str[256]; - guid_str[0] = '\0'; - SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); - - sdl_joystick_device *const devinfo = m_sixaxis_mode - ? &devicelist().create_device<sdl_sixaxis_joystick_device>(machine, name ? name : guid_str, guid_str, *this) - : &devicelist().create_device<sdl_joystick_device>(machine, name ? name : guid_str, guid_str, *this); - - if (!devinfo) + SDL_Joystick *const joy = SDL_JoystickOpen(event.jdevice.which); + if (!joy) { - SDL_JoystickClose(joy); - continue; + osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", event.jdevice.which, SDL_GetError()); } - - devinfo->sdl_state.device = joy; - devinfo->sdl_state.joystick_id = SDL_JoystickInstanceID(joy); - devinfo->sdl_state.hapdevice = SDL_HapticOpenFromJoystick(joy); -#if SDL_VERSION_ATLEAST(2, 0, 14) - char const *const serial = SDL_JoystickGetSerial(joy); - if (serial) - devinfo->sdl_state.serial = serial; else -#endif // SDL_VERSION_ATLEAST(2, 0, 14) - devinfo->sdl_state.serial = std::nullopt; - - osd_printf_verbose("Joystick: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X\n", - name ? name : "<nullptr>", - guid_str, - SDL_JoystickGetVendor(joy), - SDL_JoystickGetProduct(joy), - SDL_JoystickGetProductVersion(joy)); - osd_printf_verbose("Joystick: ... %d axes, %d buttons %d hats %d balls\n", - SDL_JoystickNumAxes(joy), - SDL_JoystickNumButtons(joy), - SDL_JoystickNumHats(joy), - SDL_JoystickNumBalls(joy)); - if (devinfo->sdl_state.hapdevice) - osd_printf_verbose("Joystick: ... Has haptic capability\n"); - else - osd_printf_verbose("Joystick: ... Does not have haptic capability\n"); - - // loop over all axes - for (int axis = 0; axis < SDL_JoystickNumAxes(joy); axis++) { - input_item_id itemid; - - if (axis < INPUT_MAX_AXIS) - itemid = (input_item_id)(ITEM_ID_XAXIS + axis); - else if (axis < INPUT_MAX_AXIS + INPUT_MAX_ADD_ABSOLUTE) - itemid = (input_item_id)(ITEM_ID_ADD_ABSOLUTE1 - INPUT_MAX_AXIS + axis); + SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); + char const *const serial = SDL_JoystickGetSerial(joy); + auto *const target_device = find_reconnect_match(guid, serial); + if (target_device) + { + auto &devinfo = dynamic_cast<sdl_joystick_device &>(*target_device); + devinfo.attach_device(joy); + } else - itemid = ITEM_ID_OTHER_AXIS_ABSOLUTE; - - snprintf(tempname, sizeof(tempname), "A%d", axis + 1); - devinfo->device()->add_item(tempname, itemid, generic_axis_get_state<std::int32_t>, &devinfo->joystick.axes[axis]); + { + SDL_JoystickClose(joy); + } } + } + else + { + dispatch_joystick_event(event); + } + } +}; - // loop over all buttons - if (SDL_JoystickNumButtons(joy) > MAX_BUTTONS) - osd_printf_verbose("Joystick: ... Has %d buttons which exceeds supported %d buttons\n", SDL_JoystickNumButtons(joy), MAX_BUTTONS); - for (int button = 0; (button < MAX_BUTTONS) && (button < SDL_JoystickNumButtons(joy)); button++) - { - input_item_id itemid; - devinfo->joystick.buttons[button] = 0; +//============================================================ +// sdl_game_controller_module +//============================================================ - if (button < INPUT_MAX_BUTTONS) - itemid = (input_item_id)(ITEM_ID_BUTTON1 + button); - else if (button < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) - itemid = (input_item_id)(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + button); - else - itemid = ITEM_ID_OTHER_SWITCH; +class sdl_game_controller_module : public sdl_joystick_module_base +{ +public: + sdl_game_controller_module() : + sdl_joystick_module_base("sdlgame"), + m_initialized_game_controller(false) + { + } - devinfo->device()->add_item(default_button_name(button), itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.buttons[button]); - } + virtual void exit() override + { + sdl_joystick_module_base::exit(); - // loop over all hats - for (int hat = 0; hat < SDL_JoystickNumHats(joy); hat++) - { - input_item_id itemid; - - snprintf(tempname, sizeof(tempname), "Hat %d Up", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1UP + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsU[hat]); - snprintf(tempname, sizeof(tempname), "Hat %d Down", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1DOWN + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsD[hat]); - snprintf(tempname, sizeof(tempname), "Hat %d Left", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1LEFT + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsL[hat]); - snprintf(tempname, sizeof(tempname), "Hat %d Right", hat + 1); - itemid = (input_item_id)((hat < INPUT_MAX_HATS) ? ITEM_ID_HAT1RIGHT + 4 * hat : ITEM_ID_OTHER_SWITCH); - devinfo->device()->add_item(tempname, itemid, generic_button_get_state<std::int32_t>, &devinfo->joystick.hatsR[hat]); - } + if (m_initialized_game_controller) + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); - // loop over all (track)balls - for (int ball = 0; ball < SDL_JoystickNumBalls(joy); ball++) - { - int itemid; + quit_joystick(); + } - if (ball * 2 < INPUT_MAX_ADD_RELATIVE) - itemid = ITEM_ID_ADD_RELATIVE1 + ball * 2; + virtual void input_init(running_machine &machine) override + { + auto &sdlopts = dynamic_cast<sdl_options const &>(*options()); + bool const sixaxis_mode = sdlopts.sixaxis(); + + if (!sdlopts.debug() && sdlopts.background_input()) + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + + init_joystick(); + if (!have_joystick()) + return; + + m_initialized_game_controller = !SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); + if (m_initialized_game_controller) + { + char const *const mapfile = sdlopts.controller_mapping_file(); + if (mapfile && *mapfile && std::strcmp(mapfile, OSDOPTVAL_NONE)) + { + auto const count = SDL_GameControllerAddMappingsFromFile(mapfile); + if (0 <= count) + osd_printf_verbose("Game Controller: %d controller mapping(s) added from file [%s].\n", count, mapfile); else - itemid = ITEM_ID_OTHER_AXIS_RELATIVE; + osd_printf_error("Game Controller: Error adding mappings from file [%s]: %s.\n", mapfile, SDL_GetError()); + } + } + else + { + osd_printf_warning("Could not initialize SDL Game Controller: %s.\n", SDL_GetError()); + } + + sdl_joystick_module_base::input_init(machine); - snprintf(tempname, sizeof(tempname), "R%d X", ball + 1); - devinfo->device()->add_item(tempname, (input_item_id)itemid, generic_axis_get_state<std::int32_t>, &devinfo->joystick.balls[ball * 2]); - snprintf(tempname, sizeof(tempname), "R%d Y", ball + 1); - devinfo->device()->add_item(tempname, (input_item_id)(itemid + 1), generic_axis_get_state<std::int32_t>, &devinfo->joystick.balls[ball * 2 + 1]); + osd_printf_verbose("Game Controller: Start initialization\n"); + for (int physical_stick = 0; physical_stick < SDL_NumJoysticks(); physical_stick++) + { + // try to open as a game controller + SDL_GameController *ctrl = nullptr; + if (m_initialized_game_controller && SDL_IsGameController(physical_stick)) + { + ctrl = SDL_GameControllerOpen(physical_stick); + if (!ctrl) + osd_printf_warning("Game Controller: Could not open SDL game controller %d: %s.\n", physical_stick, SDL_GetError()); } + + // fall back to joystick API if necessary + if (!ctrl) + create_joystick_device(physical_stick, sixaxis_mode); + else + create_game_controller_device(physical_stick, ctrl); } - static int const event_types[] = { + static int const joy_event_types[] = { int(SDL_JOYAXISMOTION), int(SDL_JOYBALLMOTION), int(SDL_JOYHATMOTION), @@ -1156,88 +2412,175 @@ public: int(SDL_JOYBUTTONUP), int(SDL_JOYDEVICEADDED), int(SDL_JOYDEVICEREMOVED) }; + static int const event_types[] = { + int(SDL_JOYAXISMOTION), + int(SDL_JOYBALLMOTION), + int(SDL_JOYHATMOTION), + int(SDL_JOYBUTTONDOWN), + int(SDL_JOYBUTTONUP), + int(SDL_JOYDEVICEADDED), + int(SDL_JOYDEVICEREMOVED), + int(SDL_CONTROLLERAXISMOTION), + int(SDL_CONTROLLERBUTTONDOWN), + int(SDL_CONTROLLERBUTTONUP), + int(SDL_CONTROLLERDEVICEADDED), + int(SDL_CONTROLLERDEVICEREMOVED) }; + if (m_initialized_game_controller) + subscribe(osd(), event_types); + else + subscribe(osd(), joy_event_types); - sdl_event_manager::instance().subscribe(event_types, this); - - osd_printf_verbose("Joystick: End initialization\n"); + osd_printf_verbose("Game Controller: End initialization\n"); } - virtual void handle_event(SDL_Event &sdlevent) override + virtual void handle_event(SDL_Event const &event) override { - if (SDL_JOYDEVICEADDED == sdlevent.type) + switch (event.type) { - SDL_Joystick *const joy = SDL_JoystickOpen(sdlevent.jdevice.which); - if (joy) + case SDL_JOYDEVICEADDED: { + // make sure this isn't an event for a reconnected game controller + auto const controller = find_joystick(SDL_JoystickGetDeviceInstanceID(event.jdevice.which)); + if (find_joystick(SDL_JoystickGetDeviceInstanceID(event.jdevice.which))) + { + osd_printf_verbose( + "Game Controller: Got SDL joystick added event for reconnected game controller %s [ID %s]\n", + controller->name(), + controller->id()); + break; + } + + SDL_Joystick *const joy = SDL_JoystickOpen(event.jdevice.which); + if (!joy) + { + osd_printf_error("Joystick: Could not open SDL joystick %d: %s.\n", event.jdevice.which, SDL_GetError()); + break; + } + SDL_JoystickGUID guid = SDL_JoystickGetGUID(joy); - 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 - auto target_device = std::find_if( - devicelist().begin(), - devicelist().end(), - [&guid_str, &serial] (auto const &device) - { - auto &devinfo = downcast<sdl_joystick_device &>(*device); - return - !devinfo.sdl_state.device && - (devinfo.id() == guid_str) && - ((serial && devinfo.sdl_state.serial && (*devinfo.sdl_state.serial == serial)) || (!serial && !devinfo.sdl_state.serial)); - }); - if (devicelist().end() != target_device) + char const *const serial = SDL_JoystickGetSerial(joy); + auto *const target_device = find_reconnect_match(guid, serial); + if (target_device) { - auto &devinfo = downcast<sdl_joystick_device &>(**target_device); - devinfo.sdl_state.device = joy; - devinfo.sdl_state.joystick_id = SDL_JoystickInstanceID(joy); - devinfo.sdl_state.hapdevice = SDL_HapticOpenFromJoystick(joy); - osd_printf_verbose("Joystick: %s [GUID %s] reconnected\n", devinfo.name(), guid_str); + // if this downcast fails, opening as a game controller worked initially but failed on reconnection + auto *const devinfo = dynamic_cast<sdl_joystick_device *>(target_device); + if (devinfo) + devinfo->attach_device(joy); + else + SDL_JoystickClose(joy); } else { SDL_JoystickClose(joy); } } - } - else - { - // Figure out which joystick this event id destined for - sdl_joystick_device *const target_device = find_joystick(sdlevent.jdevice.which); // FIXME: this depends on SDL_JoystickID being the same size as Sint32 + break; + + // for devices supported by the game controller API, this is received before the corresponding SDL_JOYDEVICEADDED + case SDL_CONTROLLERDEVICEADDED: + if (m_initialized_game_controller) + { + SDL_GameController *const ctrl = SDL_GameControllerOpen(event.cdevice.which); + if (!ctrl) + { + osd_printf_error("Game Controller: Could not open SDL game controller %d: %s.\n", event.cdevice.which, SDL_GetError()); + break; + } + + SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(event.cdevice.which); + char const *const serial = SDL_GameControllerGetSerial(ctrl); + auto *const target_device = find_reconnect_match(guid, serial); + if (target_device) + { + // downcast can fail if there was an error opening the device as a game controller the first time + auto *const devinfo = dynamic_cast<sdl_game_controller_device *>(target_device); + if (devinfo) + devinfo->attach_device(ctrl); + else + SDL_GameControllerClose(ctrl); + } + else + { + SDL_GameControllerClose(ctrl); + } + } + break; - // If we find a matching joystick, dispatch the event to the joystick - if (target_device) - target_device->queue_events(&sdlevent, 1); + default: + dispatch_joystick_event(event); } } private: - sdl_joystick_device *find_joystick(SDL_JoystickID instance) + sdl_game_controller_device *create_game_controller_device(int index, SDL_GameController *ctrl) { - for (auto &ptr : devicelist()) + // get basic info + char const *const name = SDL_GameControllerName(ctrl); + SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(index); + char guid_str[256]; + guid_str[0] = '\0'; + SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str) - 1); + char const *const serial = SDL_GameControllerGetSerial(ctrl); + std::string id(guid_str); + if (serial) + id.append(1, '-').append(serial); + + // print some diagnostic info + osd_printf_verbose("Game Controller: %s [GUID %s] Vendor ID %04X, Product ID %04X, Revision %04X, Serial %s\n", + name ? name : "<nullptr>", + guid_str, + SDL_GameControllerGetVendor(ctrl), + SDL_GameControllerGetProduct(ctrl), + SDL_GameControllerGetProductVersion(ctrl), + serial ? serial : "<nullptr>"); + char *const mapping = SDL_GameControllerMapping(ctrl); + if (mapping) { - sdl_joystick_device *const device = downcast<sdl_joystick_device *>(ptr.get()); - if (device->sdl_state.device && (device->sdl_state.joystick_id == instance)) - return device; + osd_printf_verbose("Game Controller: ... mapping [%s]\n", mapping); + SDL_free(mapping); } - return nullptr; + else + { + osd_printf_verbose("Game Controller: ... no mapping\n"); + } + + // instantiate device + sdl_game_controller_device &devinfo = create_device<sdl_game_controller_device>( + DEVICE_CLASS_JOYSTICK, + name ? name : guid_str, + guid_str, + ctrl, + serial); + return &devinfo; } + + bool m_initialized_game_controller; }; } // anonymous namespace +} // namespace osd + + #else // defined(SDLMAME_SDL2) -#include "input_module.h" +namespace osd { + +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_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdl") +MODULE_NOT_SUPPORTED(sdl_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "sdljoy") +MODULE_NOT_SUPPORTED(sdl_game_controller_module, OSD_JOYSTICKINPUT_PROVIDER, "sdlgame") + +} // anonymous namespace + +} // namespace osd #endif // defined(SDLMAME_SDL2) -MODULE_DEFINITION(KEYBOARDINPUT_SDL, sdl_keyboard_module) -MODULE_DEFINITION(MOUSEINPUT_SDL, sdl_mouse_module) -MODULE_DEFINITION(JOYSTICKINPUT_SDL, sdl_joystick_module) + +MODULE_DEFINITION(KEYBOARDINPUT_SDL, osd::sdl_keyboard_module) +MODULE_DEFINITION(MOUSEINPUT_SDL, osd::sdl_mouse_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_sdlcommon.cpp b/src/osd/modules/input/input_sdlcommon.cpp deleted file mode 100644 index 659add8dc79..00000000000 --- a/src/osd/modules/input/input_sdlcommon.cpp +++ /dev/null @@ -1,337 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes -//============================================================ -// -// input_sdlcommon.cpp - SDL Common code shared by SDL modules -// -// Note: this code is also used by the X11 input modules -// -//============================================================ - -#include "input_module.h" -#include "modules/osdmodule.h" - -#if defined(OSD_SDL) - -#include "input_sdlcommon.h" - -#include <cctype> -#include <cstddef> -#include <memory> -#include <algorithm> - -// MAME headers -#include "emu.h" - -#include "ui/uimain.h" -#include "uiinput.h" - -#include "window.h" - -#include "util/language.h" - -#include "osdepend.h" -#include "strconv.h" - -#include "../../sdl/osdsdl.h" -#include "input_common.h" - - -#define GET_WINDOW(ev) window_from_id((ev)->windowID) -//#define GET_WINDOW(ev) ((ev)->windowID) - - -static std::shared_ptr<sdl_window_info> window_from_id(Uint32 windowID) -{ - SDL_Window *sdl_window = SDL_GetWindowFromID(windowID); - - auto& windows = osd_common_t::s_window_list; - auto window = std::find_if(windows.begin(), windows.end(), [sdl_window](std::shared_ptr<osd_window> w) - { - return std::static_pointer_cast<sdl_window_info>(w)->platform_window() == sdl_window; - }); - - if (window == windows.end()) - return nullptr; - - return std::static_pointer_cast<sdl_window_info>(*window); -} - -void sdl_event_manager::process_events(running_machine &machine) -{ - std::lock_guard<std::mutex> scope_lock(m_lock); - SDL_Event sdlevent; - while (SDL_PollEvent(&sdlevent)) - { - // process window events if they come in - if (sdlevent.type == SDL_WINDOWEVENT) - process_window_event(machine, sdlevent); - - // Find all subscribers for the event type - auto subscribers = m_subscription_index.equal_range(sdlevent.type); - - // Dispatch the events - std::for_each(subscribers.first, subscribers.second, [&sdlevent](auto sub) - { - sub.second->handle_event(sdlevent); - }); - } -} - -void sdl_event_manager::process_window_event(running_machine &machine, SDL_Event &sdlevent) -{ - std::shared_ptr<sdl_window_info> window = GET_WINDOW(&sdlevent.window); - - if (window == nullptr) - { - // This condition may occur when the fullscreen toggle is used - osd_printf_verbose("Skipped window event due to missing window param from SDL\n"); - return; - } - - switch (sdlevent.window.event) - { - case SDL_WINDOWEVENT_MOVED: - window->notify_changed(); - m_focus_window = window; - break; - - case SDL_WINDOWEVENT_RESIZED: -#ifdef SDLMAME_LINUX - /* FIXME: SDL2 sends some spurious resize events on Ubuntu - * while in fullscreen mode. Ignore them for now. - */ - if (!window->fullscreen()) -#endif - { - //printf("event data1,data2 %d x %d %ld\n", event.window.data1, event.window.data2, sizeof(SDL_Event)); - window->resize(sdlevent.window.data1, sdlevent.window.data2); - } - break; - - case SDL_WINDOWEVENT_ENTER: - m_mouse_over_window = 1; - break; - - case SDL_WINDOWEVENT_LEAVE: - machine.ui_input().push_mouse_leave_event(window->target()); - m_mouse_over_window = 0; - break; - - case SDL_WINDOWEVENT_FOCUS_GAINED: - m_focus_window = window; - machine.ui_input().push_window_focus_event(window->target()); - break; - - case SDL_WINDOWEVENT_FOCUS_LOST: - machine.ui_input().push_window_defocus_event(window->target()); - break; - - case SDL_WINDOWEVENT_CLOSE: - machine.schedule_exit(); - break; - } -} - -//============================================================ -// customize_input_type_list -//============================================================ - -void sdl_osd_interface::customize_input_type_list(std::vector<input_type_entry> &typelist) -{ - input_item_id mameid_code; - input_code ui_code; - const char* uimode; - char fullmode[64]; - - // loop over the defaults - for (input_type_entry &entry : typelist) - { - switch (entry.type()) - { - // configurable UI mode switch - case IPT_UI_TOGGLE_UI: - uimode = options().ui_mode_key(); - if (!strcmp(uimode, "auto")) - { -#if defined(__APPLE__) && defined(__MACH__) - mameid_code = keyboard_trans_table::instance().lookup_mame_code("ITEM_ID_INSERT"); -#else - mameid_code = keyboard_trans_table::instance().lookup_mame_code("ITEM_ID_SCRLOCK"); -#endif - } - else - { - snprintf(fullmode, 63, "ITEM_ID_%s", uimode); - mameid_code = keyboard_trans_table::instance().lookup_mame_code(fullmode); - } - ui_code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(mameid_code)); - entry.defseq(SEQ_TYPE_STANDARD).set(ui_code); - break; - // alt-enter for fullscreen - case IPT_OSD_1: - entry.configure_osd("TOGGLE_FULLSCREEN", N_p("input-name", "Toggle Fullscreen")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_ENTER, KEYCODE_LALT); - break; - - // page down for fastforward (must be OSD_3 as per src/emu/ui.c) - case IPT_UI_FAST_FORWARD: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_PGDN); - break; - - // OSD hotkeys use LCTRL and start at F3, they start at - // F3 because F1-F2 are hardcoded into many drivers to - // various dipswitches, and pressing them together with - // LCTRL will still press/toggle these dipswitches. - - // add a Not lcrtl condition to the reset key - case IPT_UI_SOFT_RESET: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F3, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LSHIFT); - break; - - // add a Not lcrtl condition to the show gfx key - case IPT_UI_SHOW_GFX: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F4, input_seq::not_code, KEYCODE_LCONTROL); - break; - - // LCTRL-F5 to toggle OpenGL filtering - case IPT_OSD_5: - entry.configure_osd("TOGGLE_FILTER", N_p("input-name", "Toggle Filter")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F5, KEYCODE_LCONTROL); - break; - - // LCTRL-F6 to decrease OpenGL prescaling - case IPT_OSD_6: - entry.configure_osd("DECREASE_PRESCALE", N_p("input-name", "Decrease Prescaling")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F6, KEYCODE_LCONTROL); - break; - // add a Not lcrtl condition to the toggle cheat key - case IPT_UI_TOGGLE_CHEAT: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F6, input_seq::not_code, KEYCODE_LCONTROL); - break; - - // LCTRL-F7 to increase OpenGL prescaling - case IPT_OSD_7: - entry.configure_osd("INCREASE_PRESCALE", N_p("input-name", "Increase Prescaling")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F7, KEYCODE_LCONTROL); - break; - - // lshift-lalt-F12 for fullscreen video (BGFX) - case IPT_OSD_8: - entry.configure_osd("RENDER_AVI", N_p("input-name", "Record Rendered Video")); - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F12, KEYCODE_LSHIFT, KEYCODE_LALT); - break; - - // add a Not lcrtl condition to the load state key - case IPT_UI_LOAD_STATE: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F7, input_seq::not_code, KEYCODE_LCONTROL, input_seq::not_code, KEYCODE_LSHIFT); - break; - - // add a Not lcrtl condition to the throttle key - case IPT_UI_THROTTLE: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_F10, input_seq::not_code, KEYCODE_LCONTROL); - break; - - // disable the config menu if the ALT key is down - // (allows ALT-TAB to switch between apps) - case IPT_UI_CONFIGURE: - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_TAB, input_seq::not_code, KEYCODE_LALT, input_seq::not_code, KEYCODE_RALT); - break; - -#if defined(__APPLE__) && defined(__MACH__) - // 78-key Apple MacBook & Bluetooth keyboards have no right control key - case IPT_MAHJONG_SCORE: - if (entry.player() == 0) - entry.defseq(SEQ_TYPE_STANDARD).set(KEYCODE_SLASH); - break; -#endif - - // leave everything else alone - default: - break; - } - } -} - -void sdl_osd_interface::poll_inputs(running_machine &machine) -{ - m_keyboard_input->poll_if_necessary(machine); - m_mouse_input->poll_if_necessary(machine); - m_lightgun_input->poll_if_necessary(machine); - m_joystick_input->poll_if_necessary(machine); -} - -void sdl_osd_interface::release_keys() -{ - auto keybd = dynamic_cast<input_module_base*>(m_keyboard_input); - if (keybd != nullptr) - keybd->devicelist().reset_devices(); -} - -bool sdl_osd_interface::should_hide_mouse() -{ - // if we are paused, no - if (machine().paused()) - return false; - - // if neither mice nor lightguns enabled in the core, then no - if (!options().mouse() && !options().lightgun()) - return false; - - if (!sdl_event_manager::instance().mouse_over_window()) - return false; - - // otherwise, yes - return true; -} - -void sdl_osd_interface::process_events_buf() -{ - SDL_PumpEvents(); -} - -//============================================================ -// devmap_init - initializes a device_map based on -// an input option prefix and max number of devices -//============================================================ - -void device_map_t::init(running_machine &machine, const char *opt, int max_devices, const char *label) -{ - int dev; - char defname[20]; - - // The max devices the user specified, better not be bigger than the max the arrays can old - assert(max_devices <= MAX_DEVMAP_ENTRIES); - - // Initialize the map to default uninitialized values - for (dev = 0; dev < MAX_DEVMAP_ENTRIES; dev++) - { - map[dev].name.clear(); - map[dev].physical = -1; - logical[dev] = -1; - } - initialized = 0; - - // populate the device map up to the max number of devices - for (dev = 0; dev < max_devices; dev++) - { - const char *dev_name; - - // derive the parameter name from the option name and index. For instance: lightgun_index1 to lightgun_index8 - sprintf(defname, "%s%d", opt, dev + 1); - - // Get the user-specified name that matches the parameter - dev_name = machine.options().value(defname); - - // If they've specified a name and it's not "auto", treat it as a custom mapping - if (dev_name && *dev_name && strcmp(dev_name, OSDOPTVAL_AUTO)) - { - // remove the spaces from the name store it in the index - map[dev].name = remove_spaces(dev_name); - osd_printf_verbose("%s: Logical id %d: %s\n", label, dev + 1, map[dev].name); - initialized = 1; - } - } -} - -#endif diff --git a/src/osd/modules/input/input_sdlcommon.h b/src/osd/modules/input/input_sdlcommon.h deleted file mode 100644 index ca71b508f24..00000000000 --- a/src/osd/modules/input/input_sdlcommon.h +++ /dev/null @@ -1,142 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Olivier Galibert, R. Belmont, Brad Hughes -//============================================================ -// -// input_sdlcommon.h - SDL Common code shared by SDL modules -// -// Note: this code is also used by the X11 input modules -// -//============================================================ - -#ifndef MAME_OSD_INPUT_INPUT_SDLCOMMON_H -#define MAME_OSD_INPUT_INPUT_SDLCOMMON_H - -#pragma once - -// standard sdl header -#include <SDL2/SDL.h> - -#include <algorithm> -#include <memory> -#include <mutex> -#include <string> -#include <unordered_map> - -#define MAX_DEVMAP_ENTRIES 16 -#define SDL_MODULE_EVENT_BUFFER_SIZE 5 - - -struct device_map_t -{ - struct { - std::string name; - int physical; - } map[MAX_DEVMAP_ENTRIES]; - int logical[MAX_DEVMAP_ENTRIES]; - int initialized; - - void init(running_machine &machine, const char *opt, int max_devices, const char *label); -}; - -//============================================================ -// event_manager_t -//============================================================ - -class sdl_event_subscriber -{ -public: - virtual ~sdl_event_subscriber() {} - virtual void handle_event(SDL_Event &sdlevent) = 0; -}; - -template <class TSubscriber> -class event_manager_t -{ -protected: - std::mutex m_lock; - std::unordered_multimap<int, TSubscriber*> m_subscription_index; - event_manager_t() - { - } - -public: - virtual ~event_manager_t() - { - } - - template <size_t N> - void subscribe(int const (&event_types)[N], TSubscriber *subscriber) - { - std::lock_guard<std::mutex> scope_lock(m_lock); - - // Add the subscription - for (int i : event_types) - m_subscription_index.emplace(i, subscriber); - } - - void unsubscribe(TSubscriber *subscriber) - { - std::lock_guard<std::mutex> scope_lock(m_lock); - - // Remove the events that match the subscriber - for (auto it = begin(m_subscription_index); it != end(m_subscription_index);) - { - if (it->second == subscriber) - { - it = m_subscription_index.erase(it); - } - else - { - ++it; - } - } - } - - virtual void process_events(running_machine &machine) = 0; -}; - -class sdl_window_info; - -class sdl_event_manager : public event_manager_t<sdl_event_subscriber> -{ -private: - bool m_mouse_over_window; - std::shared_ptr<sdl_window_info> m_focus_window; - - sdl_event_manager() - : m_mouse_over_window(true), - m_focus_window(nullptr) - { - } - -public: - bool mouse_over_window() const { return m_mouse_over_window; } - bool has_focus() const { return m_focus_window != nullptr; } - std::shared_ptr<sdl_window_info> focus_window() const { return m_focus_window; } - - static sdl_event_manager& instance() - { - static sdl_event_manager s_instance; - return s_instance; - } - - void process_events(running_machine &machine) override; - -private: - void process_window_event(running_machine &machine, SDL_Event &sdlevent); -}; - -//============================================================ -// INLINE FUNCTIONS -//============================================================ - -static inline std::string remove_spaces(const char *s) -{ - // Remove the spaces - auto output = std::string(s); - output.erase(std::remove_if(output.begin(), output.end(), isspace), output.end()); - - return output; -} - -#endif // MAME_OSD_INPUT_INPUT_SDLCOMMON_H diff --git a/src/osd/modules/input/input_win32.cpp b/src/osd/modules/input/input_win32.cpp index 459e624fcfa..3803bc2a180 100644 --- a/src/osd/modules/input/input_win32.cpp +++ b/src/osd/modules/input/input_win32.cpp @@ -10,16 +10,24 @@ #if defined(OSD_WINDOWS) -#include "emu.h" - #include "input_windows.h" +#include "input_wincommon.h" + +// osd/windows +#include "window.h" + +// emu +#include "inpttype.h" + #include "strconv.h" // standard windows headers #include <tchar.h> +namespace osd { + namespace { //============================================================ @@ -30,115 +38,113 @@ namespace { class win32_keyboard_device : public event_based_device<KeyPressEventArgs> { public: - keyboard_state keyboard; + win32_keyboard_device(std::string &&name, std::string &&id, input_module &module) : + event_based_device(std::move(name), std::move(id), module), + m_keyboard({{0}}) + { + } - win32_keyboard_device(running_machine& machine, std::string &&name, std::string &&id, input_module &module) - : event_based_device(machine, std::move(name), std::move(id), DEVICE_CLASS_KEYBOARD, module), - keyboard({{0}}) + virtual void reset() override { + memset(&m_keyboard, 0, sizeof(m_keyboard)); } - void reset() override + virtual void configure(input_device &device) override { - memset(&keyboard, 0, sizeof(keyboard)); + keyboard_trans_table const &table = keyboard_trans_table::instance(); + + for (int keynum = 0; keynum < MAX_KEYS; keynum++) + { + input_item_id itemid = table.map_di_scancode_to_itemid(keynum); + 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); + + // add the item to the device + device.add_item( + name, + util::string_format("SCAN%03d", keynum), + itemid, + generic_button_get_state<std::uint8_t>, + &m_keyboard.state[keynum]); + } } protected: - void process_event(KeyPressEventArgs &args) override + virtual void process_event(KeyPressEventArgs const &args) override { - keyboard.state[args.scancode] = args.event_id == INPUT_EVENT_KEYDOWN ? 0x80 : 0x00; + m_keyboard.state[args.scancode] = args.event_id == INPUT_EVENT_KEYDOWN ? 0x80 : 0x00; } + +private: + keyboard_state m_keyboard; }; + //============================================================ // keyboard_input_win32 - win32 keyboard input module //============================================================ -class keyboard_input_win32 : public wininput_module +class keyboard_input_win32 : public wininput_module<win32_keyboard_device> { -private: - public: - keyboard_input_win32() - : wininput_module(OSD_KEYBOARDINPUT_PROVIDER, "win32") + keyboard_input_win32() : wininput_module<win32_keyboard_device>(OSD_KEYBOARDINPUT_PROVIDER, "win32") { } virtual void input_init(running_machine &machine) override { - // Add a single win32 keyboard device that we'll monitor using Win32 - auto &devinfo = devicelist().create_device<win32_keyboard_device>(machine, "Win32 Keyboard 1", "Win32 Keyboard 1", *this); - - keyboard_trans_table &table = keyboard_trans_table::instance(); - - // populate it - for (int keynum = 0; keynum < MAX_KEYS; keynum++) - { - input_item_id itemid = table.map_di_scancode_to_itemid(keynum); - TCHAR keyname[100]; + wininput_module<win32_keyboard_device>::input_init(machine); - // generate the name - 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 = osd::text::from_tstring(keyname); - - // add the item to the device - devinfo.device()->add_item(name, itemid, generic_button_get_state<std::uint8_t>, &devinfo.keyboard.state[keynum]); - } + // Add a single win32 keyboard device that we'll monitor using Win32 + create_device<win32_keyboard_device>(DEVICE_CLASS_KEYBOARD, "Win32 Keyboard 1", "Win32 Keyboard 1"); } - bool handle_input_event(input_event eventid, void *eventdata) override + virtual bool handle_input_event(input_event eventid, void *eventdata) override { - if (!input_enabled()) - return false; - - KeyPressEventArgs *args; - switch (eventid) { - case INPUT_EVENT_KEYDOWN: - case INPUT_EVENT_KEYUP: - args = static_cast<KeyPressEventArgs*>(eventdata); - devicelist().for_each_device([args](auto device) - { - auto keyboard = dynamic_cast<win32_keyboard_device*>(device); - if (keyboard != nullptr) - keyboard->queue_events(args, 1); - }); - - return true; - - default: - return false; + case INPUT_EVENT_KEYDOWN: + case INPUT_EVENT_KEYUP: + devicelist().for_each_device( + [args = static_cast<KeyPressEventArgs const *>(eventdata)] (auto &device) + { + device.queue_events(args, 1); + }); + return false; // we still want text input events to be generated + + default: + return false; } } }; + //============================================================ // win32_mouse_device //============================================================ -struct win32_mouse_state -{ - POINT last_point; -}; - class win32_mouse_device : public event_based_device<MouseButtonEventArgs> { public: - mouse_state mouse; - win32_mouse_state win32_mouse; - - win32_mouse_device(running_machine& machine, std::string &&name, std::string &&id, input_module &module) - : event_based_device(machine, std::move(name), std::move(id), DEVICE_CLASS_MOUSE, module), - mouse({0}), - win32_mouse({{0}}) + 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}}) { } - void poll() override + virtual void poll(bool relative_reset) override { - event_based_device::poll(); + event_based_device::poll(relative_reset); + + if (!relative_reset) + return; CURSORINFO cursor_info = {0}; cursor_info.cbSize = sizeof(CURSORINFO); @@ -149,140 +155,188 @@ public: if (!(cursor_info.flags & CURSOR_SHOWING)) { // We measure the position change from the previously set center position - mouse.lX = (cursor_info.ptScreenPos.x - win32_mouse.last_point.x) * osd::INPUT_RELATIVE_PER_PIXEL; - mouse.lY = (cursor_info.ptScreenPos.y - win32_mouse.last_point.y) * osd::INPUT_RELATIVE_PER_PIXEL; + m_mouse.lX = (cursor_info.ptScreenPos.x - m_win32_mouse.last_point.x) * input_device::RELATIVE_PER_PIXEL; + m_mouse.lY = (cursor_info.ptScreenPos.y - m_win32_mouse.last_point.y) * input_device::RELATIVE_PER_PIXEL; RECT window_pos = {0}; - GetWindowRect(std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(), &window_pos); + GetWindowRect( + dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(), + &window_pos); // We reset the cursor position to the middle of the window each frame - win32_mouse.last_point.x = window_pos.left + (window_pos.right - window_pos.left) / 2; - win32_mouse.last_point.y = window_pos.top + (window_pos.bottom - window_pos.top) / 2; + m_win32_mouse.last_point.x = window_pos.left + (window_pos.right - window_pos.left) / 2; + m_win32_mouse.last_point.y = window_pos.top + (window_pos.bottom - window_pos.top) / 2; - SetCursorPos(win32_mouse.last_point.x, win32_mouse.last_point.y); + SetCursorPos(m_win32_mouse.last_point.x, m_win32_mouse.last_point.y); } } - void reset() override + virtual void configure(input_device &device) override { - memset(&mouse, 0, sizeof(mouse)); - memset(&win32_mouse, 0, sizeof(win32_mouse)); + // 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); + } + + // populate the buttons + for (int butnum = 0; butnum < 2; butnum++) + { + device.add_item( + default_button_name(butnum), + std::string_view(), + input_item_id(ITEM_ID_BUTTON1 + butnum), + generic_button_get_state<BYTE>, + &m_mouse.rgbButtons[butnum]); + } + } + + virtual void reset() override + { + memset(&m_mouse, 0, sizeof(m_mouse)); + memset(&m_win32_mouse, 0, sizeof(m_win32_mouse)); } protected: - void process_event(MouseButtonEventArgs &args) override + virtual void process_event(MouseButtonEventArgs const &args) override { // set the button state - mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00; - - // Make sure we have a fresh mouse position on button down - if (args.keydown) - module().poll_if_necessary(machine()); + m_mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00; } + +private: + struct win32_mouse_state + { + POINT last_point; + }; + + mouse_state m_mouse; + win32_mouse_state m_win32_mouse; }; + //============================================================ // mouse_input_win32 - win32 mouse input module //============================================================ -class mouse_input_win32 : public wininput_module +class mouse_input_win32 : public wininput_module<win32_mouse_device> { public: - mouse_input_win32() - : wininput_module(OSD_MOUSEINPUT_PROVIDER, "win32") + mouse_input_win32() : wininput_module<win32_mouse_device>(OSD_MOUSEINPUT_PROVIDER, "win32") { } virtual void input_init(running_machine &machine) override { - if (!input_enabled() || !mouse_enabled()) + wininput_module<win32_mouse_device>::input_init(machine); + + if (!options()->mouse()) return; // allocate a device - auto &devinfo = devicelist().create_device<win32_mouse_device>(machine, "Win32 Mouse 1", "Win32 Mouse 1", *this); + 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 + { + 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); }); + + return true; + } +}; + +//============================================================ +// win32_lightgun_device_base +//============================================================ + +class win32_lightgun_device_base : public event_based_device<MouseButtonEventArgs> +{ +public: + virtual void reset() override + { + memset(&m_mouse, 0, sizeof(m_mouse)); + } + + virtual void configure(input_device &device) override + { // populate the axes for (int axisnum = 0; axisnum < 2; axisnum++) { - devinfo.device()->add_item( - default_axis_name[axisnum], - static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &devinfo.mouse.lX + 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); } // populate the buttons for (int butnum = 0; butnum < 2; butnum++) { - devinfo.device()->add_item( - default_button_name(butnum), - static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state<BYTE>, - &devinfo.mouse.rgbButtons[butnum]); + device.add_item( + default_button_name(butnum), + std::string_view(), + input_item_id(ITEM_ID_BUTTON1 + butnum), + generic_button_get_state<BYTE>, + &m_mouse.rgbButtons[butnum]); } } - bool handle_input_event(input_event eventid, void *eventdata) 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 }) { - if (!input_enabled() || !mouse_enabled() || eventid != INPUT_EVENT_MOUSE_BUTTON) - return false; - - auto args = static_cast<MouseButtonEventArgs*>(eventdata); - devicelist().for_each_device([args](auto device) - { - auto mouse = dynamic_cast<win32_mouse_device*>(device); - if (mouse != nullptr) - mouse->queue_events(args, 1); - }); - - return true; } + + mouse_state m_mouse; }; + + //============================================================ // win32_lightgun_device //============================================================ -class win32_lightgun_device : public event_based_device<MouseButtonEventArgs> +class win32_lightgun_device : public win32_lightgun_device_base { -private: - BOOL m_lightgun_shared_axis_mode; - int m_gun_index; - public: - mouse_state mouse; - - win32_lightgun_device(running_machine& machine, std::string &&name, std::string &&id, input_module &module) - : event_based_device(machine, std::move(name), std::move(id), DEVICE_CLASS_LIGHTGUN, module), - m_lightgun_shared_axis_mode(FALSE), - m_gun_index(0), - mouse({0}) + win32_lightgun_device( + std::string &&name, + std::string &&id, + input_module &module) : + win32_lightgun_device_base(std::move(name), std::move(id), module) { - m_lightgun_shared_axis_mode = downcast<windows_options &>(machine.options()).dual_lightgun(); - - // Since we are about to be added to the list, the current size is the zero-based index of where we will be - m_gun_index = downcast<wininput_module&>(module).devicelist().size(); } - void poll() override + virtual void poll(bool relative_reset) override { - event_based_device::poll(); + event_based_device::poll(relative_reset); int32_t xpos = 0, ypos = 0; - POINT mousepos; - - // if we are using the shared axis hack, the data is updated via Windows messages only - if (m_lightgun_shared_axis_mode) - return; // get the cursor position and transform into final results + POINT mousepos; GetCursorPos(&mousepos); - if (!osd_common_t::s_window_list.empty()) + if (!osd_common_t::window_list().empty()) { - RECT client_rect; - // get the position relative to the window - HWND hwnd = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(); + HWND const hwnd = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); + RECT client_rect; GetClientRect(hwnd, &client_rect); ScreenToClient(hwnd, &mousepos); @@ -292,91 +346,93 @@ public: } // update the X/Y positions - mouse.lX = xpos; - mouse.lY = ypos; + m_mouse.lX = xpos; + m_mouse.lY = ypos; } - void reset() override +protected: + virtual void process_event(MouseButtonEventArgs const &args) override { - memset(&mouse, 0, sizeof(mouse)); + // In non-shared axis mode, just update the button state + m_mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00; } +}; -protected: - void process_event(MouseButtonEventArgs &args) override + +//============================================================ +// win32_dual_lightgun_device +//============================================================ + +class win32_dual_lightgun_device : public win32_lightgun_device_base +{ +public: + win32_dual_lightgun_device( + std::string &&name, + std::string &&id, + input_module &module, + int index) : + win32_lightgun_device_base(std::move(name), std::move(id), module), + m_gun_index(index) { - // Are we in shared axis mode? - if (m_lightgun_shared_axis_mode) - { - handle_shared_axis_mode(args); - } - else - { - // In non-shared axis mode, just update the button state - mouse.rgbButtons[args.button] = args.keydown ? 0x80 : 0x00; - } } -private: - void handle_shared_axis_mode(MouseButtonEventArgs &args) +protected: + virtual void process_event(MouseButtonEventArgs const &args) override { - int button = args.button; + int const button = args.button; - // We only handle the first four buttons in shared axis mode if (button > 3) - return; - - // First gun doesn't handle buttons 2 & 3 - if (button >= 2 && m_gun_index == 0) - return; - - // Second gun doesn't handle buttons 0 & 1 - if (button < 2 && m_gun_index == 1) - return; + 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 logical_button = m_gun_index == 1 ? button - 2 : button; + int const logical_button = (m_gun_index == 1) ? (button - 2) : button; // set the button state - mouse.rgbButtons[logical_button] = args.keydown ? 0x80 : 0x00; + m_mouse.rgbButtons[logical_button] = args.keydown ? 0x80 : 0x00; if (args.keydown) { - RECT client_rect; - POINT mousepos; - // get the position relative to the window - HWND hwnd = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_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 - mouse.lX = normalize_absolute_axis(mousepos.x, client_rect.left, client_rect.right); - mouse.lY = normalize_absolute_axis(mousepos.y, client_rect.top, client_rect.bottom); + 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); } } + +private: + int const m_gun_index; }; + //============================================================ // lightgun_input_win32 - win32 lightgun input module //============================================================ -class lightgun_input_win32 : public wininput_module +class lightgun_input_win32 : public wininput_module<win32_lightgun_device_base> { public: - lightgun_input_win32() - : wininput_module(OSD_LIGHTGUNINPUT_PROVIDER, "win32") - { - } - - int init_internal() override + lightgun_input_win32() : wininput_module<win32_lightgun_device_base>(OSD_LIGHTGUNINPUT_PROVIDER, "win32") { - return 0; } virtual void input_init(running_machine &machine) override { - int max_guns = downcast<windows_options&>(machine.options()).dual_lightgun() ? 2 : 1; + wininput_module<win32_lightgun_device_base>::input_init(machine); + + bool const shared_axis_mode = dynamic_cast<windows_options const &>(*options()).dual_lightgun(); + int const max_guns = shared_axis_mode ? 2 : 1; // allocate the lightgun devices for (int gunnum = 0; gunnum < max_guns; gunnum++) @@ -384,42 +440,21 @@ public: static const char *const gun_names[] = { "Win32 Gun 1", "Win32 Gun 2" }; // allocate a device - auto &devinfo = devicelist().create_device<win32_lightgun_device>(machine, gun_names[gunnum], gun_names[gunnum], *this); - - // populate the axes - for (int axisnum = 0; axisnum < 2; axisnum++) - { - devinfo.device()->add_item( - default_axis_name[axisnum], - static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), - generic_axis_get_state<LONG>, - &devinfo.mouse.lX + axisnum); - } - - // populate the buttons - for (int butnum = 0; butnum < 2; butnum++) - { - devinfo.device()->add_item( - default_button_name(butnum), - static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state<BYTE>, - &devinfo.mouse.rgbButtons[butnum]); - } + if (shared_axis_mode) + create_device<win32_dual_lightgun_device>(DEVICE_CLASS_LIGHTGUN, gun_names[gunnum], gun_names[gunnum], gunnum); + else + create_device<win32_lightgun_device>(DEVICE_CLASS_LIGHTGUN, gun_names[gunnum], gun_names[gunnum]); } } - bool handle_input_event(input_event eventid, void* eventdata) override + virtual bool handle_input_event(input_event eventid, void *eventdata) override { - if (!input_enabled() || !lightgun_enabled() || eventid != INPUT_EVENT_MOUSE_BUTTON) + if (!manager().class_enabled(DEVICE_CLASS_LIGHTGUN) || eventid != INPUT_EVENT_MOUSE_BUTTON) return false; - auto args = static_cast<MouseButtonEventArgs*>(eventdata); - devicelist().for_each_device([args](auto device) - { - auto lightgun = dynamic_cast<win32_lightgun_device*>(device); - if (lightgun != nullptr) - lightgun->queue_events(args, 1); - }); + auto const *const args = static_cast<MouseButtonEventArgs *>(eventdata); + devicelist().for_each_device( + [args] (auto &device) { device.queue_events(args, 1); }); return true; } @@ -427,14 +462,24 @@ public: } // anonymous namespace +} // namespace osd + #else // defined(OSD_WINDOWS) +namespace osd { + +namespace { + MODULE_NOT_SUPPORTED(keyboard_input_win32, OSD_KEYBOARDINPUT_PROVIDER, "win32") MODULE_NOT_SUPPORTED(mouse_input_win32, OSD_MOUSEINPUT_PROVIDER, "win32") MODULE_NOT_SUPPORTED(lightgun_input_win32, OSD_LIGHTGUNINPUT_PROVIDER, "win32") +} // anonymous namespace + +} // namespace osd + #endif // defined(OSD_WINDOWS) -MODULE_DEFINITION(KEYBOARDINPUT_WIN32, keyboard_input_win32) -MODULE_DEFINITION(MOUSEINPUT_WIN32, mouse_input_win32) -MODULE_DEFINITION(LIGHTGUNINPUT_WIN32, lightgun_input_win32) +MODULE_DEFINITION(KEYBOARDINPUT_WIN32, osd::keyboard_input_win32) +MODULE_DEFINITION(MOUSEINPUT_WIN32, osd::mouse_input_win32) +MODULE_DEFINITION(LIGHTGUNINPUT_WIN32, osd::lightgun_input_win32) diff --git a/src/osd/modules/input/input_wincommon.h b/src/osd/modules/input/input_wincommon.h new file mode 100644 index 00000000000..d77ab8dce9a --- /dev/null +++ b/src/osd/modules/input/input_wincommon.h @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// input_wincommon.h - Common code used by Windows input modules +// +//============================================================ +#ifndef MAME_OSD_INPUT_INPUT_WINCOMMON_H +#define MAME_OSD_INPUT_INPUT_WINCOMMON_H + +#pragma once + +#include "input_common.h" + +#include <windows.h> + + +namespace osd { + +// state information for a keyboard +struct keyboard_state +{ + uint8_t state[MAX_KEYS]; + int8_t oldkey[MAX_KEYS]; + int8_t currkey[MAX_KEYS]; +}; + +// state information for a mouse (matches DIMOUSESTATE exactly) +struct mouse_state +{ + LONG lX; + LONG lY; + LONG lZ; + BYTE rgbButtons[8]; +}; + +} // namespace osd + +#endif // MAME_OSD_INPUT_INPUT_WINCOMMON_H diff --git a/src/osd/modules/input/input_windows.cpp b/src/osd/modules/input/input_windows.cpp index 60306765f72..c635ff0b553 100644 --- a/src/osd/modules/input/input_windows.cpp +++ b/src/osd/modules/input/input_windows.cpp @@ -25,82 +25,76 @@ bool windows_osd_interface::should_hide_mouse() const { - bool hidemouse = false; - wininput_module *mod; + if (!winwindow_has_focus()) + return false; - mod = dynamic_cast<wininput_module *>(m_keyboard_input); - if (mod) hidemouse |= mod->should_hide_mouse(); + if (machine().paused()) + return false; - mod = dynamic_cast<wininput_module *>(m_mouse_input); - if (mod) hidemouse |= mod->should_hide_mouse(); + // track if mouse/lightgun is enabled, for mouse hiding purposes + bool const mouse_enabled = machine().input().class_enabled(DEVICE_CLASS_MOUSE); + bool const lightgun_enabled = machine().input().class_enabled(DEVICE_CLASS_LIGHTGUN); + if (!mouse_enabled && !lightgun_enabled) + return false; - mod = dynamic_cast<wininput_module *>(m_lightgun_input); - if (mod) hidemouse |= mod->should_hide_mouse(); - - mod = dynamic_cast<wininput_module *>(m_joystick_input); - if (mod) hidemouse |= mod->should_hide_mouse(); - - return hidemouse; + return true; } bool windows_osd_interface::handle_input_event(input_event eventid, void *eventdata) const { bool handled = false; - wininput_module *mod; + wininput_event_handler *mod; - mod = dynamic_cast<wininput_module *>(m_keyboard_input); - if (mod) handled |= mod->handle_input_event(eventid, eventdata); + mod = dynamic_cast<wininput_event_handler *>(m_keyboard_input); + if (mod) + handled |= mod->handle_input_event(eventid, eventdata); - mod = dynamic_cast<wininput_module *>(m_mouse_input); - if (mod) handled |= mod->handle_input_event(eventid, eventdata); + mod = dynamic_cast<wininput_event_handler *>(m_mouse_input); + if (mod) + handled |= mod->handle_input_event(eventid, eventdata); - mod = dynamic_cast<wininput_module *>(m_lightgun_input); - if (mod) handled |= mod->handle_input_event(eventid, eventdata); + mod = dynamic_cast<wininput_event_handler *>(m_lightgun_input); + if (mod) + handled |= mod->handle_input_event(eventid, eventdata); - mod = dynamic_cast<wininput_module *>(m_joystick_input); - if (mod) handled |= mod->handle_input_event(eventid, eventdata); + mod = dynamic_cast<wininput_event_handler *>(m_joystick_input); + if (mod) + handled |= mod->handle_input_event(eventid, eventdata); return handled; } -void windows_osd_interface::poll_input(running_machine &machine) const -{ - m_keyboard_input->poll_if_necessary(machine); - m_mouse_input->poll_if_necessary(machine); - m_lightgun_input->poll_if_necessary(machine); - m_joystick_input->poll_if_necessary(machine); -} - //============================================================ // customize_input_type_list //============================================================ void windows_osd_interface::customize_input_type_list(std::vector<input_type_entry> &typelist) { - const char* uimode; - // loop over the defaults for (input_type_entry &entry : typelist) switch (entry.type()) { // disable the config menu if the ALT key is down // (allows ALT-TAB to switch between windows apps) - case IPT_UI_CONFIGURE: + 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: - uimode = options().ui_mode_key(); - if (strcmp(uimode, "auto")) { - std::string fullmode = "ITEM_ID_"; - fullmode += uimode; - input_item_id const mameid_code = keyboard_trans_table::instance().lookup_mame_code(fullmode.c_str()); - if (ITEM_ID_INVALID != mameid_code) + char const *const uimode = options().ui_mode_key(); + if (uimode && *uimode && strcmp(uimode, "auto")) { - input_code const ui_code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(mameid_code)); - entry.defseq(SEQ_TYPE_STANDARD).set(ui_code); + std::string fullmode("ITEM_ID_"); + fullmode.append(uimode); + input_item_id const mameid_code = keyboard_trans_table::instance().lookup_mame_code(fullmode.c_str()); + if (ITEM_ID_INVALID != mameid_code) + { + input_code const ui_code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, input_item_id(mameid_code)); + entry.defseq(SEQ_TYPE_STANDARD).set(ui_code); + } } } break; @@ -138,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 7acf8586876..175151cb4e5 100644 --- a/src/osd/modules/input/input_windows.h +++ b/src/osd/modules/input/input_windows.h @@ -23,64 +23,26 @@ // TYPEDEFS //============================================================ -// state information for a keyboard -struct keyboard_state -{ - uint8_t state[MAX_KEYS]; - int8_t oldkey[MAX_KEYS]; - int8_t currkey[MAX_KEYS]; -}; - -// state information for a mouse (matches DIMOUSESTATE exactly) -struct mouse_state -{ - LONG lX; - LONG lY; - LONG lZ; - BYTE rgbButtons[8]; -}; -class wininput_module : public input_module_base +class wininput_event_handler { protected: - bool m_global_inputs_enabled = false; + wininput_event_handler() = default; + virtual ~wininput_event_handler() = default; public: - wininput_module(const char *type, const char *name) : input_module_base(type, name) { } - - virtual ~wininput_module() { } - - virtual bool should_hide_mouse() + virtual bool handle_input_event(input_event eventid, void *data) { - if (winwindow_has_focus() // has focus - && (!video_config.windowed || !osd_common_t::s_window_list.front()->win_has_menu()) // not windowed or doesn't have a menu - && (input_enabled() && !input_paused()) // input enabled and not paused - && (mouse_enabled() || lightgun_enabled())) // either mouse or lightgun enabled in the core - { - return true; - } - return false; } +}; - virtual bool handle_input_event(input_event eventid, void* data) - { - return false; - } +template <typename Info> +class wininput_module : public input_module_impl<Info, osd_common_t>, public wininput_event_handler +{ protected: - - void before_poll(running_machine& machine) override - { - // periodically process events, in case they're not coming through - // this also will make sure the mouse state is up-to-date - winwindow_process_events_periodic(machine); - } - - bool should_poll_devices(running_machine &machine) override - { - return input_enabled() && (m_global_inputs_enabled || winwindow_has_focus()); - } + using input_module_impl<Info, osd_common_t>::input_module_impl; }; #endif // MAME_OSD_INPUT_INPUT_WINDOWS_H diff --git a/src/osd/modules/input/input_winhybrid.cpp b/src/osd/modules/input/input_winhybrid.cpp index 52d991a9674..2f1023e24fb 100644 --- a/src/osd/modules/input/input_winhybrid.cpp +++ b/src/osd/modules/input/input_winhybrid.cpp @@ -8,23 +8,20 @@ #include "modules/osdmodule.h" -#if defined(OSD_WINDOWS) - -#include "emu.h" +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include "input_dinput.h" #include "input_xinput.h" -#include <list> #include <vector> #include <oleauto.h> #include <wbemcli.h> -namespace { +namespace osd { -using namespace Microsoft::WRL; +namespace { template <class TCom> class ComArray @@ -42,7 +39,7 @@ public: Release(); } - TCom** ReleaseAndGetAddressOf() + TCom **ReleaseAndGetAddressOf() { Release(); @@ -50,7 +47,7 @@ public: return &m_entries[0]; } - TCom* operator [] (int i) + TCom *operator[](int i) { return m_entries[i]; } @@ -64,7 +61,7 @@ public: { for (auto &entry : m_entries) { - if (entry != nullptr) + if (entry) { entry->Release(); entry = nullptr; @@ -119,28 +116,24 @@ public: typedef std::unique_ptr<OLECHAR, bstr_deleter> bstr_ptr; + //============================================================ // winhybrid_joystick_module //============================================================ -class winhybrid_joystick_module : public wininput_module, public device_enum_interface +class winhybrid_joystick_module : public input_module_impl<device_info, osd_common_t> { private: - std::shared_ptr<xinput_api_helper> m_xinput_helper; + std::unique_ptr<xinput_api_helper> m_xinput_helper; std::unique_ptr<dinput_api_helper> m_dinput_helper; - std::list<DWORD> m_xinput_deviceids; - bool m_xinput_detect_failed; public: winhybrid_joystick_module() : - wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "winhybrid"), - m_xinput_helper(nullptr), - m_dinput_helper(nullptr), - m_xinput_detect_failed(false) + input_module_impl<device_info, osd_common_t>(OSD_JOYSTICKINPUT_PROVIDER, "winhybrid") { } - bool probe() override + virtual bool probe() override { int status = init_helpers(); if (status != 0) @@ -152,10 +145,12 @@ public: return true; } - int init(const osd_options &options) override + virtual int init(osd_interface &osd, const osd_options &options) override { + int status; + // Call the base - int status = wininput_module::init(options); + status = input_module_impl<device_info, osd_common_t>::init(osd, options); if (status != 0) return status; @@ -170,98 +165,100 @@ public: return 0; } - BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override - { - dinput_cooperative_level cooperative_level = dinput_cooperative_level::FOREGROUND; - running_machine &machine = *static_cast<running_machine *>(ref); - dinput_joystick_device *devinfo; - int result = 0; - - // First check if this device is XInput Compatible. If so, don't add it here - // as it'll be picked up by Xinput - if (!m_xinput_detect_failed && is_xinput_device(&instance->guidProduct)) - { - osd_printf_verbose("Skipping DirectInput for XInput compatible joystick %S.\n", instance->tszInstanceName); - goto exit; - } - - if (!osd_common_t::s_window_list.empty() && osd_common_t::s_window_list.front()->win_has_menu()) - cooperative_level = dinput_cooperative_level::BACKGROUND; - - // allocate and link in a new device - devinfo = m_dinput_helper->create_device<dinput_joystick_device>(machine, *this, instance, &c_dfDIJoystick, nullptr, cooperative_level); - if (devinfo == nullptr) - goto exit; - - result = devinfo->configure(); - if (result != 0) - { - osd_printf_error("Failed to configure DI Joystick device. Error 0x%x\n", static_cast<unsigned int>(result)); - } - - exit: - return DIENUM_CONTINUE; - } - - void exit() override - { - m_xinput_helper.reset(); - m_dinput_helper.reset(); - - wininput_module::exit(); - } - -protected: virtual void input_init(running_machine &machine) override { - HRESULT result = get_xinput_devices(m_xinput_deviceids); + input_module_impl<device_info, osd_common_t>::input_init(machine); + + bool xinput_detect_failed = false; + std::vector<DWORD> xinput_deviceids; + HRESULT result = get_xinput_devices(xinput_deviceids); if (result != 0) { - m_xinput_detect_failed = true; + xinput_detect_failed = true; + xinput_deviceids.clear(); osd_printf_warning("XInput device detection failed. XInput won't be used. Error: 0x%X\n", uint32_t(result)); } - // Enumerate all the directinput joysticks and add them if they aren't xinput compatible - result = m_dinput_helper->enum_attached_devices(DI8DEVCLASS_GAMECTRL, this, &machine); - if (result != DI_OK) - fatalerror("DirectInput: Unable to enumerate game controllers (result=%08X)\n", uint32_t(result)); + // Enumerate all the DirectInput joysticks and add them if they aren't XInput compatible + result = m_dinput_helper->enum_attached_devices( + DI8DEVCLASS_GAMECTRL, + [this, &xinput_deviceids] (LPCDIDEVICEINSTANCE instance) + { + // First check if this device is XInput compatible. + // If so, don't add it here as it'll be picked up by Xinput. + auto const found = std::find( + xinput_deviceids.begin(), + xinput_deviceids.end(), + instance->guidProduct.Data1); + if (xinput_deviceids.end() != found) + { + osd_printf_verbose("Skipping DirectInput for XInput compatible joystick %S.\n", instance->tszInstanceName); + return DIENUM_CONTINUE; + } - xinput_joystick_device *devinfo; + // allocate and link in a new device + auto devinfo = m_dinput_helper->create_device<dinput_joystick_device>( + *this, + instance, + &c_dfDIJoystick, + nullptr, + background_input() ? dinput_cooperative_level::BACKGROUND : dinput_cooperative_level::FOREGROUND, + [] (auto const &device, auto const &format) -> bool + { + // set absolute mode + HRESULT const result = dinput_api_helper::set_dword_property( + device, + DIPROP_AXISMODE, + 0, + DIPH_DEVICE, + DIPROPAXISMODE_ABS); + if ((result != DI_OK) && (result != DI_PROPNOEFFECT)) + { + osd_printf_error("DirectInput: Unable to set absolute mode for joystick.\n"); + return false; + } + return true; + }); + if (devinfo) + add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo)); + + return DIENUM_CONTINUE; + }); + if (result != DI_OK) + fatalerror("DirectInput: Unable to enumerate game controllers (result=%08X).\n", uint32_t(result)); // now add all xinput devices - if (!m_xinput_detect_failed) + if (!xinput_detect_failed) { // Loop through each gamepad to determine if they are connected for (UINT i = 0; i < XUSER_MAX_COUNT; i++) { - XINPUT_STATE state = { 0 }; - - if (m_xinput_helper->xinput_get_state(i, &state) == ERROR_SUCCESS) - { - // allocate and link in a new device - devinfo = m_xinput_helper->create_xinput_device(machine, i, *this); - if (!devinfo) - continue; - - // Configure each gamepad to add buttons and Axes, etc. - devinfo->configure(); - } + // allocate and link in a new device + auto devinfo = m_xinput_helper->create_xinput_device(i, *this); + if (devinfo) + add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo)); } } } + virtual void exit() override + { + input_module_impl<device_info, osd_common_t>::exit(); + + m_xinput_helper.reset(); + m_dinput_helper.reset(); + } + private: int init_helpers() { - int status = 0; - if (!m_xinput_helper) { - m_xinput_helper = std::make_shared<xinput_api_helper>(); - status = m_xinput_helper->initialize(); + m_xinput_helper = std::make_unique<xinput_api_helper>(); + int const status = m_xinput_helper->initialize(); if (status != 0) { - osd_printf_verbose("xinput_api_helper failed to initialize! Error: %u\n", static_cast<unsigned int>(status)); + osd_printf_verbose("Failed to initialize XInput API! Error: %u\n", static_cast<unsigned int>(status)); return -1; } } @@ -269,30 +266,15 @@ private: if (!m_dinput_helper) { m_dinput_helper = std::make_unique<dinput_api_helper>(); - status = m_dinput_helper->initialize(); + int const status = m_dinput_helper->initialize(); if (status != DI_OK) { - osd_printf_verbose("dinput_api_helper failed to initialize! Error: %u\n", static_cast<unsigned int>(status)); + osd_printf_verbose("Failed to initialize DirectInput API! Error: %u\n", static_cast<unsigned int>(status)); return -1; } } - return status; - } - - //----------------------------------------------------------------------------- - // Returns true if the DirectInput device is also an XInput device. - //----------------------------------------------------------------------------- - bool is_xinput_device(const GUID* pGuidProductFromDirectInput) - { - // Check each xinput device to see if this device's vid/pid matches - for (auto devid = m_xinput_deviceids.begin(); devid != m_xinput_deviceids.end(); ++devid) - { - if (*devid == pGuidProductFromDirectInput->Data1) - return true; - } - - return false; + return 0; } //----------------------------------------------------------------------------- @@ -302,54 +284,43 @@ private: // Checking against a VID/PID of 0x028E/0x045E won't find 3rd party or future // XInput devices. //----------------------------------------------------------------------------- - HRESULT get_xinput_devices(std::list<DWORD> &xinput_id_list) const + HRESULT get_xinput_devices(std::vector<DWORD> &xinput_deviceids) { - ComPtr<IWbemServices> pIWbemServices; - ComPtr<IEnumWbemClassObject> pEnumDevices; - ComPtr<IWbemLocator> pIWbemLocator; - ComArray<IWbemClassObject> pDevices(20); - bstr_ptr bstrDeviceID; - bstr_ptr bstrClassName; - bstr_ptr bstrNamespace; - DWORD uReturned = 0; - UINT iDevice; - variant_wrapper var; - HRESULT hr; + xinput_deviceids.clear(); - // CoInit if needed - CoInitialize(nullptr); + HRESULT hr; // Create WMI + Microsoft::WRL::ComPtr<IWbemLocator> pIWbemLocator; hr = CoCreateInstance( - __uuidof(WbemLocator), - nullptr, - CLSCTX_INPROC_SERVER, - __uuidof(IWbemLocator), - reinterpret_cast<void**>(pIWbemLocator.GetAddressOf())); - - if (FAILED(hr) || pIWbemLocator == nullptr) + __uuidof(WbemLocator), + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), + reinterpret_cast<void **>(pIWbemLocator.GetAddressOf())); + if (FAILED(hr) || !pIWbemLocator) { osd_printf_error("Creating WbemLocator failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); return hr; } // Create BSTRs for WMI - bstrNamespace = bstr_ptr(SysAllocString(L"\\\\.\\root\\cimv2")); - bstrDeviceID = bstr_ptr(SysAllocString(L"DeviceID")); - bstrClassName = bstr_ptr(SysAllocString(L"Win32_PNPEntity")); + bstr_ptr bstrNamespace = bstr_ptr(SysAllocString(L"\\\\.\\root\\cimv2")); + bstr_ptr bstrDeviceID = bstr_ptr(SysAllocString(L"DeviceID")); + bstr_ptr bstrClassName = bstr_ptr(SysAllocString(L"Win32_PNPEntity")); // Connect to WMI + Microsoft::WRL::ComPtr<IWbemServices> pIWbemServices; hr = pIWbemLocator->ConnectServer( - bstrNamespace.get(), - nullptr, - nullptr, - nullptr, - 0L, - nullptr, - nullptr, - pIWbemServices.GetAddressOf()); - - if (FAILED(hr) || pIWbemServices == nullptr) + bstrNamespace.get(), + nullptr, + nullptr, + nullptr, + 0L, + nullptr, + nullptr, + pIWbemServices.GetAddressOf()); + if (FAILED(hr) || !pIWbemServices) { osd_printf_error("Connecting to WMI Server failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); return hr; @@ -357,38 +328,41 @@ private: // Switch security level to IMPERSONATE (void)CoSetProxyBlanket( - pIWbemServices.Get(), - RPC_C_AUTHN_WINNT, - RPC_C_AUTHZ_NONE, - nullptr, - RPC_C_AUTHN_LEVEL_CALL, - RPC_C_IMP_LEVEL_IMPERSONATE, - nullptr, - 0); + pIWbemServices.Get(), + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + nullptr, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + nullptr, + 0); // Get list of Win32_PNPEntity devices + Microsoft::WRL::ComPtr<IEnumWbemClassObject> pEnumDevices; hr = pIWbemServices->CreateInstanceEnum(bstrClassName.get(), 0, nullptr, pEnumDevices.GetAddressOf()); - if (FAILED(hr) || pEnumDevices == nullptr) + if (FAILED(hr) || !pEnumDevices) { osd_printf_error("Getting list of Win32_PNPEntity devices failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); return hr; } // Loop over all devices - for (; ; ) + ComArray<IWbemClassObject> pDevices(20); + variant_wrapper var; + for ( ; ; ) { // Get a few at a time + DWORD uReturned = 0; hr = pEnumDevices->Next(10000, pDevices.Size(), pDevices.ReleaseAndGetAddressOf(), &uReturned); if (FAILED(hr)) { osd_printf_error("Enumerating WMI classes failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); return hr; } - if (uReturned == 0) break; - for (iDevice = 0; iDevice < uReturned; iDevice++) + for (UINT iDevice = 0; iDevice < uReturned; iDevice++) { if (!pDevices[iDevice]) continue; @@ -402,19 +376,18 @@ private: if (wcsstr(var.Get().bstrVal, L"IG_")) { // If it does, then get the VID/PID from var.bstrVal - DWORD dwPid = 0, dwVid = 0; - WCHAR* strVid = wcsstr(var.Get().bstrVal, L"VID_"); + DWORD dwVid = 0; + WCHAR const *const strVid = wcsstr(var.Get().bstrVal, L"VID_"); if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) dwVid = 0; - WCHAR* strPid = wcsstr(var.Get().bstrVal, L"PID_"); + DWORD dwPid = 0; + WCHAR const *const strPid = wcsstr(var.Get().bstrVal, L"PID_"); if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) dwPid = 0; - DWORD dwVidPid = MAKELONG(dwVid, dwPid); - - // Add the VID/PID to a linked list - xinput_id_list.push_back(dwVidPid); + // Add the VID/PID to a list + xinput_deviceids.push_back(MAKELONG(dwVid, dwPid)); } } } @@ -429,12 +402,14 @@ private: } // anonymous namespace -#else // defined(OSD_WINDOWS) +} // namespace osd + +#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include "input_module.h" -MODULE_NOT_SUPPORTED(winhybrid_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "winhybrid") +namespace osd { namespace { MODULE_NOT_SUPPORTED(winhybrid_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "winhybrid") } } -#endif // defined(OSD_WINDOWS) +#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -MODULE_DEFINITION(JOYSTICKINPUT_WINHYBRID, winhybrid_joystick_module) +MODULE_DEFINITION(JOYSTICKINPUT_WINHYBRID, osd::winhybrid_joystick_module) diff --git a/src/osd/modules/input/input_x11.cpp b/src/osd/modules/input/input_x11.cpp index 0cd0caa64df..5230dfd7164 100644 --- a/src/osd/modules/input/input_x11.cpp +++ b/src/osd/modules/input/input_x11.cpp @@ -9,35 +9,39 @@ //============================================================ #include "input_module.h" + #include "modules/osdmodule.h" #if defined(SDLMAME_SDL2) && !defined(SDLMAME_WIN32) && defined(USE_XINPUT) && USE_XINPUT +#include "input_common.h" + +#include "sdl/osdsdl.h" + +// MAME headers +#include "inpttype.h" + +// standard SDL header +#include <SDL2/SDL.h> + // for X11 xinput #include <X11/Xlib.h> #include <X11/extensions/XInput.h> #include <X11/Xutil.h> -// standard sdl header -#include <SDL2/SDL.h> +#include <algorithm> #include <cctype> #include <cstddef> -#include <mutex> +#include <cstdio> +#include <cstring> #include <memory> +#include <mutex> #include <queue> #include <string> -#include <algorithm> - -// MAME headers -#include "emu.h" -#include "osdepend.h" +#include <unordered_map> -// MAMEOS headers -#include "../lib/osdobj_common.h" -#include "input_common.h" -#include "../../sdl/osdsdl.h" -#include "input_sdlcommon.h" +namespace osd { namespace { @@ -52,20 +56,6 @@ static int key_release_type = INVALID_EVENT_TYPE; static int proximity_in_type = INVALID_EVENT_TYPE; static int proximity_out_type = INVALID_EVENT_TYPE; -// state information for a lightgun -struct lightgun_state -{ - int32_t lX, lY; - int32_t buttons[MAX_BUTTONS]; -}; - -struct x11_api_state -{ - XID deviceid; // X11 device id - int32_t maxx, maxy; - int32_t minx, miny; -}; - //============================================================ // DEBUG MACROS //============================================================ @@ -74,56 +64,64 @@ struct x11_api_state #define XI_DBG(format, ...) osd_printf_verbose(format, __VA_ARGS__) #define print_motion_event(motion) print_motion_event_impl(motion) -inline void print_motion_event_impl(XDeviceMotionEvent *motion) +inline void print_motion_event_impl(XDeviceMotionEvent const *motion) { /* * print a lot of debug informations of the motion event(s). */ osd_printf_verbose( - "XDeviceMotionEvent:\n" - " type: %d\n" - " serial: %lu\n" - " send_event: %d\n" - " display: %p\n" - " window: --\n" - " deviceid: %lu\n" - " root: --\n" - " subwindow: --\n" - " time: --\n" - " x: %d, y: %d\n" - " x_root: %d, y_root: %d\n" - " state: %u\n" - " is_hint: %2.2X\n" - " same_screen: %d\n" - " device_state: %u\n" - " axes_count: %2.2X\n" - " first_axis: %2.2X\n" - " axis_data[6]: {%d,%d,%d,%d,%d,%d}\n", - motion->type, - motion->serial, - motion->send_event, - motion->display, - /* motion->window, */ - motion->deviceid, - /* motion->root */ - /* motion->subwindow */ - /* motion->time, */ - motion->x, motion->y, - motion->x_root, motion->y_root, - motion->state, - motion->is_hint, - motion->same_screen, - motion->device_state, - motion->axes_count, - motion->first_axis, - motion->axis_data[0], motion->axis_data[1], motion->axis_data[2], motion->axis_data[3], motion->axis_data[4], motion->axis_data[5] - ); + "XDeviceMotionEvent:\n" + " type: %d\n" + " serial: %lu\n" + " send_event: %d\n" + " display: %p\n" + " window: --\n" + " deviceid: %lu\n" + " root: --\n" + " subwindow: --\n" + " time: --\n" + " x: %d, y: %d\n" + " x_root: %d, y_root: %d\n" + " state: %u\n" + " is_hint: %2.2X\n" + " same_screen: %d\n" + " device_state: %u\n" + " axes_count: %2.2X\n" + " first_axis: %2.2X\n" + " axis_data[6]: {%d,%d,%d,%d,%d,%d}\n", + motion->type, + motion->serial, + motion->send_event, + motion->display, + /* motion->window, */ + motion->deviceid, + /* motion->root */ + /* motion->subwindow */ + /* motion->time, */ + motion->x, motion->y, + motion->x_root, motion->y_root, + motion->state, + motion->is_hint, + motion->same_screen, + motion->device_state, + motion->axes_count, + motion->first_axis, + motion->axis_data[0], motion->axis_data[1], motion->axis_data[2], motion->axis_data[3], motion->axis_data[4], motion->axis_data[5]); } #else -#define XI_DBG(format, ...) while(0) {} -#define print_motion_event(motion) while(0) {} +#define XI_DBG(format, ...) do { } while (false) +#define print_motion_event(motion) do { } while (false) #endif + +inline std::string remove_spaces(const char *s) +{ + std::string output(s); + output.erase(std::remove_if(output.begin(), output.end(), isspace), output.end()); + return output; +} + + //============================================================ // lightgun helpers: copy-past from xinfo //============================================================ @@ -141,7 +139,7 @@ find_device_info(Display *display, bool is_id = true; XID id = static_cast<XID>(-1); - for(loop = 0; loop < len; loop++) + for (loop = 0; loop < len; loop++) { if (!isdigit(name[loop])) { @@ -157,7 +155,7 @@ find_device_info(Display *display, devices = XListInputDevices(display, &num_devices); - for(loop = 0; loop < num_devices; loop++) + for (loop = 0; loop < num_devices; loop++) { osd_printf_verbose("Evaluating device with name: %s\n", devices[loop].name); @@ -255,19 +253,64 @@ register_events( return number; } -//============================================================ -// x11_event_manager -//============================================================ -class x11_event_handler +struct device_map { -public: - virtual ~x11_event_handler() {} + static inline constexpr unsigned MAX_ENTRIES = 16; + + struct { + std::string name; + int physical; + } map[MAX_ENTRIES]; + int logical[MAX_ENTRIES]; + int initialized; + + void init(osd_options const &options, const char *opt, int max_devices, const char *label) + { + // initialize based on an input option prefix and max number of devices + char defname[20]; + + // The max devices the user specified, better not be bigger than the max the arrays can old + assert(max_devices <= MAX_ENTRIES); + + // Initialize the map to default uninitialized values + for (int dev = 0; dev < MAX_ENTRIES; dev++) + { + map[dev].name.clear(); + map[dev].physical = -1; + logical[dev] = -1; + } + initialized = 0; + + // populate the device map up to the max number of devices + for (int dev = 0; dev < max_devices; dev++) + { + const char *dev_name; + + // derive the parameter name from the option name and index. For instance: lightgun_index1 to lightgun_index8 + sprintf(defname, "%s%d", opt, dev + 1); + + // Get the user-specified name that matches the parameter + dev_name = options.value(defname); - virtual void handle_event(XEvent &xevent) = 0; + // If they've specified a name and it's not "auto", treat it as a custom mapping + if (dev_name && *dev_name && strcmp(dev_name, OSDOPTVAL_AUTO)) + { + // remove the spaces from the name store it in the index + map[dev].name = remove_spaces(dev_name); + osd_printf_verbose("%s: Logical id %d: %s\n", label, dev + 1, map[dev].name); + initialized = 1; + } + } + } }; -class x11_event_manager : public event_manager_t<x11_event_handler> + +//============================================================ +// x11_event_manager +//============================================================ + +class x11_event_manager : public event_subscription_manager<XEvent, int> { private: struct x_cleanup @@ -288,14 +331,12 @@ private: x_ptr<Display> m_display; - x11_event_manager() : event_manager_t() - { - } + x11_event_manager() = default; public: - Display * display() const { return m_display.get(); } + Display *display() const { return m_display.get(); } - static x11_event_manager& instance() + static x11_event_manager &instance() { static x11_event_manager s_instance; return s_instance; @@ -303,7 +344,7 @@ public: int initialize() { - std::lock_guard<std::mutex> scope_lock(m_lock); + std::lock_guard<std::mutex> scope_lock(subscription_mutex()); if (m_display) return 0; @@ -325,31 +366,24 @@ public: return 0; } - void process_events(running_machine &machine) override + void process_events() { - std::lock_guard<std::mutex> scope_lock(m_lock); - XEvent xevent; + std::lock_guard<std::mutex> scope_lock(subscription_mutex()); // If X11 has become invalid for some reason, XPending will crash. Assert instead. assert(m_display); - //Get XInput events + // Get XInput events while (XPending(m_display.get()) != 0) { - XNextEvent(m_display.get(), &xevent); - - // Find all subscribers for the event type - auto const subscribers = m_subscription_index.equal_range(xevent.type); - - // Dispatch the events - std::for_each( - subscribers.first, - subscribers.second, - [&xevent] (auto &pair) { pair.second->handle_event(xevent); }); + XEvent event; + XNextEvent(m_display.get(), &event); + dispatch_event(event.type, event); } } }; + //============================================================ // x11_input_device //============================================================ @@ -357,15 +391,23 @@ public: class x11_input_device : public event_based_device<XEvent> { public: - x11_api_state x11_state; - - x11_input_device(running_machine &machine, std::string &&name, std::string &&id, input_device_class devclass, input_module &module) : - event_based_device(machine, std::move(name), std::move(id), devclass, module), - x11_state({0}) + x11_input_device( + std::string &&name, + std::string &&id, + input_module &module, + XDeviceInfo const *info) : + event_based_device(std::move(name), std::move(id), module), + m_device_id(info ? info->id : 0) { } + + XID const &device_id() const { return m_device_id; } + +protected: + XID const m_device_id; // X11 device ID }; + //============================================================ // x11_lightgun_device //============================================================ @@ -373,55 +415,124 @@ public: class x11_lightgun_device : public x11_input_device { public: - lightgun_state lightgun; + x11_lightgun_device( + std::string &&name, + std::string &&id, + input_module + &module, + XDeviceInfo *info) : + x11_input_device(std::move(name), std::move(id), module, info), + m_axis_count(0), + m_button_count(0), + m_maxx(0), + m_maxy(0), + m_minx(0), + m_miny(0), + m_lightgun({ 0 }) + { + if (info && (info->num_classes > 0)) + { + // Grab device info and translate to stuff MAME can use + XAnyClassPtr any = static_cast<XAnyClassPtr>(info->inputclassinfo); + for (int i = 0; i < info->num_classes; i++) + { + switch (any->c_class) + { + // Set the axis min/max ranges if we got them + case ValuatorClass: + { + auto const valuator_info = reinterpret_cast<XValuatorInfoPtr>(any); + auto axis_info = reinterpret_cast<XAxisInfoPtr>(reinterpret_cast<char *>(valuator_info) + sizeof(XValuatorInfo)); + for (int j = 0; (j < valuator_info->num_axes) && (j < 2); j++, axis_info++) + { + if (j == 0) + { + XI_DBG("Set minx=%d, maxx=%d\n", axis_info->min_value, axis_info->max_value); + m_axis_count = 1; + m_maxx = axis_info->max_value; + m_minx = axis_info->min_value; + } + + if (j == 1) + { + XI_DBG("Set miny=%d, maxy=%d\n", axis_info->min_value, axis_info->max_value); + m_axis_count = 2; + m_maxy = axis_info->max_value; + m_miny = axis_info->min_value; + } + } + } + break; + + // Count the lightgun buttons based on what we read + case ButtonClass: + { + XButtonInfoPtr b = reinterpret_cast<XButtonInfoPtr>(any); + if (b->num_buttons < 0) + m_button_count = 0; + else if (b->num_buttons <= MAX_BUTTONS) + m_button_count = b->num_buttons; + else + m_button_count = MAX_BUTTONS; + } + break; + } - x11_lightgun_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module) : - x11_input_device(machine, std::move(name), std::move(id), DEVICE_CLASS_LIGHTGUN, module), - lightgun({0}) + any = reinterpret_cast<XAnyClassPtr>(reinterpret_cast<char *>(any) + any->length); + } + } + } + + virtual void reset() override + { + memset(&m_lightgun, 0, sizeof(m_lightgun)); + } + + virtual void configure(input_device &device) override { + // Add buttons + for (int button = 0; button < m_button_count; button++) + { + input_item_id const itemid = input_item_id(ITEM_ID_BUTTON1 + button); + device.add_item(default_button_name(button), std::string_view(), itemid, generic_button_get_state<std::int32_t>, &m_lightgun.buttons[button]); + } + + // Add X and Y axis + if (1 <= m_axis_count) + device.add_item("X", std::string_view(), ITEM_ID_XAXIS, generic_axis_get_state<std::int32_t>, &m_lightgun.lX); + if (2 <= m_axis_count) + device.add_item("Y", std::string_view(), ITEM_ID_YAXIS, generic_axis_get_state<std::int32_t>, &m_lightgun.lY); } - void process_event(XEvent &xevent) override + virtual void process_event(XEvent const &xevent) override { if (xevent.type == motion_type) { - XDeviceMotionEvent *motion = reinterpret_cast<XDeviceMotionEvent *>(&xevent); + auto const motion = reinterpret_cast<XDeviceMotionEvent const *>(&xevent); print_motion_event(motion); - /* - * We have to check with axis will start on array index 0. - * We have also to check the number of axes that are stored in the array. - */ + // We have to check with axis will start on array index 0. + // We also have to check the number of axes that are stored in the array. switch (motion->first_axis) { - /* - * Starting with x, check number of axis, if there is also the y axis stored. - */ + // Starting with x, check number of axes, if there is also the y axis stored. case 0: if (motion->axes_count >= 1) - { - lightgun.lX = normalize_absolute_axis(motion->axis_data[0], x11_state.minx, x11_state.maxx); - if (motion->axes_count >= 2) - { - lightgun.lY = normalize_absolute_axis(motion->axis_data[1], x11_state.miny, x11_state.maxy); - } - } + m_lightgun.lX = normalize_absolute_axis(motion->axis_data[0], m_minx, m_maxx); + if (motion->axes_count >= 2) + m_lightgun.lY = normalize_absolute_axis(motion->axis_data[1], m_miny, m_maxy); break; - /* - * Starting with y, ... - */ + // Starting with y, ... case 1: if (motion->axes_count >= 1) - { - lightgun.lY = normalize_absolute_axis(motion->axis_data[0], x11_state.miny, x11_state.maxy); - } + m_lightgun.lY = normalize_absolute_axis(motion->axis_data[0], m_miny, m_maxy); break; } } else if (xevent.type == button_press_type || xevent.type == button_release_type) { - XDeviceButtonEvent *button = reinterpret_cast<XDeviceButtonEvent *>(&xevent); + auto const button = reinterpret_cast<XDeviceButtonEvent const *>(&xevent); /* * SDL/X11 Number the buttons 1,2,3, while windows and other parts of MAME @@ -429,37 +540,50 @@ public: * -1 the button number to align the numbering schemes. */ int button_number = button->button; - switch (button_number) + if (button_number <= MAX_BUTTONS) { + switch (button_number) + { case 2: - button_number = 3; - break; case 3: - button_number = 2; + button_number ^= 1; break; + } + m_lightgun.buttons[button_number - 1] = (xevent.type == button_press_type) ? 0x80 : 0; } - lightgun.buttons[button_number - 1] = (xevent.type == button_press_type) ? 0x80 : 0; } } - void reset() override +private: + struct lightgun_state { - memset(&lightgun, 0, sizeof(lightgun)); - } + int32_t lX, lY; + int32_t buttons[MAX_BUTTONS]; + }; + + int m_axis_count; + int m_button_count; + int32_t m_maxx; + int32_t m_maxy; + int32_t m_minx; + int32_t m_miny; + lightgun_state m_lightgun; }; + //============================================================ // x11_lightgun_module //============================================================ -class x11_lightgun_module : public input_module_base, public x11_event_handler +class x11_lightgun_module : public input_module_impl<x11_input_device, osd_common_t>, public x11_event_manager::subscriber { private: - device_map_t m_lightgun_map; - Display * m_display; + device_map m_lightgun_map; + Display *m_display; + public: x11_lightgun_module() : - input_module_base(OSD_LIGHTGUNINPUT_PROVIDER, "x11"), + input_module_impl<x11_input_device, osd_common_t>(OSD_LIGHTGUNINPUT_PROVIDER, "x11"), m_display(nullptr) { } @@ -467,113 +591,109 @@ public: virtual bool probe() override { // If there is no X server, X11 lightguns cannot be supported - if (!XOpenDisplay(nullptr)) - { + Display *const display = XOpenDisplay(nullptr); + if (!display) return false; - } + XCloseDisplay(display); return true; } - void input_init(running_machine &machine) override + virtual int init(osd_interface &osd, osd_options const &options) override { - osd_printf_verbose("Lightgun: Begin initialization\n"); - - m_lightgun_map.init(machine, SDLOPTION_LIGHTGUNINDEX, 8, "Lightgun mapping"); - + // If the X server has become invalid, a crash can occur x11_event_manager::instance().initialize(); m_display = x11_event_manager::instance().display(); + if (!m_display) + return -1; - // If the X server has become invalid, a crash can occur - assert(m_display != nullptr); + return input_module_impl<x11_input_device, osd_common_t>::init(osd, options); + } + + virtual void input_init(running_machine &machine) override + { + assert(m_display); + + input_module_impl<x11_input_device, osd_common_t>::input_init(machine); + + osd_printf_verbose("Lightgun: Begin initialization\n"); + + m_lightgun_map.init(*options(), SDLOPTION_LIGHTGUNINDEX, 8, "Lightgun mapping"); // Loop through all 8 possible devices for (int index = 0; index < 8; index++) { - XDeviceInfo *info; - // Skip if the name is empty - if (m_lightgun_map.map[index].name.length() == 0) + if (m_lightgun_map.map[index].name.empty()) continue; + // Find the device info associated with the name std::string const &name = m_lightgun_map.map[index].name; - char defname[512]; - - // Register and add the device - auto *devinfo = create_lightgun_device(machine, index); osd_printf_verbose("%i: %s\n", index, name); + XDeviceInfo *const info = find_device_info(m_display, name.c_str(), 0); + if (!info) + osd_printf_verbose("Lightgun: Can't find device %s!\n", name); - // Find the device info associated with the name - info = find_device_info(m_display, name.c_str(), 0); + // previously had code to use "NC%d" format if name was empty + // but that couldn't happen because device creation would be skipped - // If we couldn't find the device, skip - if (!info) - { - osd_printf_verbose("Can't find device %s!\n", name); - continue; - } + // Register and add the device + create_device<x11_lightgun_device>( + DEVICE_CLASS_LIGHTGUN, + std::string(name), + std::string(name), + info); - //Grab device info and translate to stuff mame can use - if (info->num_classes > 0) + // Register this device to receive event notifications + if (info) { - // Add the lightgun buttons based on what we read - add_lightgun_buttons(static_cast<XAnyClassPtr>(info->inputclassinfo), info->num_classes, *devinfo); - - // Also, set the axix min/max ranges if we got them - set_lightgun_axis_props(static_cast<XAnyClassPtr>(info->inputclassinfo), info->num_classes, *devinfo); + int const events_registered = register_events(m_display, info, name.c_str(), 0); + osd_printf_verbose("Device %i: Registered %i events.\n", int(info->id), events_registered); } + } - // Add X and Y axis - sprintf(defname, "X %s", devinfo->name().c_str()); - devinfo->device()->add_item(defname, ITEM_ID_XAXIS, generic_axis_get_state<std::int32_t>, &devinfo->lightgun.lX); - - sprintf(defname, "Y %s", devinfo->name().c_str()); - devinfo->device()->add_item(defname, ITEM_ID_YAXIS, generic_axis_get_state<std::int32_t>, &devinfo->lightgun.lY); - - // Save the device id - devinfo->x11_state.deviceid = info->id; + // register ourself to handle events from event manager + int const event_types[] = { motion_type, button_press_type, button_release_type }; + osd_printf_verbose("Events types to register: motion:%d, press:%d, release:%d\n", motion_type, button_press_type, button_release_type); + subscribe(x11_event_manager::instance(), event_types); - // Register this device to receive event notifications - int events_registered = register_events(m_display, info, m_lightgun_map.map[index].name.c_str(), 0); - osd_printf_verbose("Device %i: Registered %i events.\n", static_cast<int>(info->id), events_registered); + osd_printf_verbose("Lightgun: End initialization\n"); + } - // register ourself to handle events from event manager - int const event_types[] = { motion_type, button_press_type, button_release_type }; - osd_printf_verbose("Events types to register: motion:%d, press:%d, release:%d\n", motion_type, button_press_type, button_release_type); - x11_event_manager::instance().subscribe(event_types, this); - } + virtual void exit() override + { + // unsubscribe from events + unsubscribe(); - osd_printf_verbose("Lightgun: End initialization\n"); + input_module_impl<x11_input_device, osd_common_t>::exit(); } - bool should_poll_devices(running_machine &machine) override + virtual bool should_poll_devices() override { - return sdl_event_manager::instance().has_focus(); + return osd().has_focus(); } - void before_poll(running_machine &machine) override + virtual void before_poll() override { - if (!should_poll_devices(machine)) - return; + // trigger the SDL event manager so it can process window events + input_module_impl<x11_input_device, osd_common_t>::before_poll(); // Tell the event manager to process events and push them to the devices - x11_event_manager::instance().process_events(machine); - - // Also trigger the SDL event manager so it can process window events - sdl_event_manager::instance().process_events(machine); + if (should_poll_devices()) + x11_event_manager::instance().process_events(); } - void handle_event(XEvent &xevent) override + virtual void handle_event(XEvent const &xevent) override { XID deviceid; if (xevent.type == motion_type) { - XDeviceMotionEvent *motion = reinterpret_cast<XDeviceMotionEvent *>(&xevent); + auto const motion = reinterpret_cast<XDeviceMotionEvent const *>(&xevent); deviceid = motion->deviceid; } else if (xevent.type == button_press_type || xevent.type == button_release_type) { - XDeviceButtonEvent *button = reinterpret_cast<XDeviceButtonEvent *>(&xevent); + auto const button = reinterpret_cast<XDeviceButtonEvent const *>(&xevent); deviceid = button->deviceid; } else @@ -582,102 +702,27 @@ public: } // Figure out which lightgun this event id destined for - auto target_device = std::find_if(devicelist().begin(), devicelist().end(), [deviceid](auto &device) - { - std::unique_ptr<device_info> &ptr = device; - return downcast<x11_input_device*>(ptr.get())->x11_state.deviceid == deviceid; - }); + auto target_device = std::find_if( + devicelist().begin(), + devicelist().end(), + [&deviceid] (auto &device) { return device->device_id() == deviceid; }); // If we find a matching lightgun, dispatch the event to the lightgun if (target_device != devicelist().end()) - { - downcast<x11_input_device*>((*target_device).get())->queue_events(&xevent, 1); - } - } - -private: - x11_lightgun_device *create_lightgun_device(running_machine &machine, int index) - { - if (m_lightgun_map.map[index].name.length() == 0) - { - if (m_lightgun_map.initialized) - { - char tempname[20]; - snprintf(tempname, std::size(tempname), "NC%d", index); - return &devicelist().create_device<x11_lightgun_device>(machine, tempname, tempname, *this); - } - else - { - return nullptr; - } - } - - return &devicelist().create_device<x11_lightgun_device>(machine, std::string(m_lightgun_map.map[index].name), std::string(m_lightgun_map.map[index].name), *this); - } - - void add_lightgun_buttons(XAnyClassPtr first_info_class, int num_classes, x11_lightgun_device &devinfo) const - { - XAnyClassPtr any = first_info_class; - - for (int i = 0; i < num_classes; i++) - { - switch (any->c_class) - { - case ButtonClass: - XButtonInfoPtr b = reinterpret_cast<XButtonInfoPtr>(any); - for (int button = 0; button < b->num_buttons; button++) - { - input_item_id itemid = static_cast<input_item_id>(ITEM_ID_BUTTON1 + button); - devinfo.device()->add_item(default_button_name(button), itemid, generic_button_get_state<std::int32_t>, &devinfo.lightgun.buttons[button]); - } - break; - } - - any = reinterpret_cast<XAnyClassPtr>(reinterpret_cast<char *>(any) + any->length); - } - } - - void set_lightgun_axis_props(XAnyClassPtr first_info_class, int num_classes, x11_lightgun_device &devinfo) const - { - XAnyClassPtr any = first_info_class; - - for (int i = 0; i < num_classes; i++) - { - switch (any->c_class) - { - case ValuatorClass: - XValuatorInfoPtr valuator_info = reinterpret_cast<XValuatorInfoPtr>(any); - XAxisInfoPtr axis_info = reinterpret_cast<XAxisInfoPtr>(reinterpret_cast<char *>(valuator_info) + sizeof(XValuatorInfo)); - for (int j = 0; j < valuator_info->num_axes; j++, axis_info++) - { - if (j == 0) - { - XI_DBG("Set minx=%d, maxx=%d\n", axis_info->min_value, axis_info->max_value); - devinfo.x11_state.maxx = axis_info->max_value; - devinfo.x11_state.minx = axis_info->min_value; - } - - if (j == 1) - { - XI_DBG("Set miny=%d, maxy=%d\n", axis_info->min_value, axis_info->max_value); - devinfo.x11_state.maxy = axis_info->max_value; - devinfo.x11_state.miny = axis_info->min_value; - } - } - break; - } - - any = reinterpret_cast<XAnyClassPtr>(reinterpret_cast<char *>(any) + any->length); - } + (*target_device)->queue_events(&xevent, 1); } }; } // anonymous namespace +} // namespace osd + + #else // defined(SDLMAME_SDL2) && !defined(SDLMAME_WIN32) && defined(USE_XINPUT) && USE_XINPUT -MODULE_NOT_SUPPORTED(x11_lightgun_module, OSD_LIGHTGUNINPUT_PROVIDER, "x11") +namespace osd { namespace { MODULE_NOT_SUPPORTED(x11_lightgun_module, OSD_LIGHTGUNINPUT_PROVIDER, "x11") } } #endif // defined(SDLMAME_SDL2) && !defined(SDLMAME_WIN32) && defined(USE_XINPUT) && USE_XINPUT -MODULE_DEFINITION(LIGHTGUN_X11, x11_lightgun_module) + +MODULE_DEFINITION(LIGHTGUN_X11, osd::x11_lightgun_module) diff --git a/src/osd/modules/input/input_xinput.cpp b/src/osd/modules/input/input_xinput.cpp index 22171895c97..474ef6fe7ff 100644 --- a/src/osd/modules/input/input_xinput.cpp +++ b/src/osd/modules/input/input_xinput.cpp @@ -1,307 +1,3141 @@ // license:BSD-3-Clause -// copyright-holders:Brad Hughes +// copyright-holders:Brad Hughes, Vas Crabb //============================================================ // // input_xinput.cpp - XInput API input support for Windows // //============================================================ +/* -#include "modules/osdmodule.h" +XInput is infamously inflexible. It currently only supports a single +controller type (game controller) with a fixed report format. The +report format includes: +* 16-bit field for single-bit buttons and D-pad directions (two bits + undefined) +* Two 8-bit axis values, used for triggers, pedals, buttons or throttle + and rudder controls, depending on the controller +* Four 16-bit axis values, used for joysticks, steering wheels or hat + switches, depending on the controller + +Some button names have changed at various times: +* Face buttons White and Black became shoulder buttons LB and RB, + respectively. +* Navigation buttons Start and Back became Menu and View, respectively. + +Subtype Gamepad Wheel Arcade stick Arcade pad Flight stick Dance pad Guitar Drum kit + +D-pad D-pad D-pad Joystick D-pad Foot switch D-Pad/Strum D-pad +L trigger Trigger Brake Button Button Rudder Pickup select^ +R trigger Trigger Accelerator Button Button Throttle ^ +L stick X Stick Wheel Roll +L stick Y Stick Pitch +R stick X Stick Hat left/right Whammy bar +R stick Y Stick Hat up/down Orientation + +A Button Button Button Button Primary fire Foot switch Fret 1 Floor tom +B Button Button Button Button Secondary fire Foot switch Fret 2 Snare +X Button Button Button Button Button Foot switch Fret 4 Low tom +Y Button Button Button Button Button Foot switch Fret 3 High tom +LB Button Button Button^ Button Button^ Fret 5 Bass drum +RB Button Button Button^ Button Button^ Button^ Button^ +LSB Button Button^ Button^ Button^ Button^ Fret modifier^ Button^ +RSB Button Button^ Button^ Button^ Button^ Button^ Button^ + +^ optional + + +At least the vast majority of controllers report 8-bit trigger +resolution and 10-bit stick resolution, even when the physical controls +use digital switches. Resolution can't be used to reliably detect +nominal analog axes controlled by switches. + +Some arcade sticks report unknown or gamepad subtype, but have a single +digital joystick with a switch to select between controlling the D-pad, +left stick and right stick. You can't assume that all three can be +controlled at the same time. + +Many controllers don't correctly report the absence of analog sticks. + + +Some controllers use capabilities to indicate extended controller type +information: + + Type Sub LSX LSY RSX +Band Hero Wireless Guitar 0x01 0x07 0x1430 0x0705 0x0001 +Band Hero Wireless Drum Kit 0x01 0x08 0x1430 0x0805 0x0001 +DJ Hero 2 Turntable 0x01 0x17 0x1430 0x1705 0x0001 +Rock Band 3 Wireless Keyboard 0x01 0x0f 0x1bad 0x1330 0x0004 + + +There are multiple physical button layouts for arcade sticks, for +example: + +Gamester Xbox Arcade Stick, Hori Real Arcade Pro EX +LT X Y LB +RT A B RB + +PXN 0082 Arcade Stick +LB X Y RB +LT A B RT + +Mortal Kombat Tournament Edition Arcade Stick + X Y + RT +LB A B + +Hori Fighting Stick EX2 + B X Y +A LT RT + +Hori Real Arcade Pro VX-SA Kai, Razer Atrox + B X Y LB +A LT RT RB + +Hori Real Arcade Pro.V Kai, Mad Catz EGO Arcade Stick, Mayflash F300, Mayflash F500 + X Y RB LB +A B RT LT + +Mad Catz WWE All Stars Brawl Stick + X Y LB LT +A B RB RT + +Arcade pads typically have six face buttons, and come with different +layouts corresponding to the latter two arcade stick layouts, with the +rightmost column on the shoulder buttons. Examples of face button +layouts: + +Hori Fighting Commander OCTA, Mad Catz Street Fighter IV FightPad, PowerA FUSION Wired FightPad +X Y RB +A B RT + +Hori Pad EX Turbo 2, Mad Catz WWE All Stars BrawlPad, Mortal Kombat X Fight Pad, PDP Versus Fighting Pad +X Y LB +A B RB -#if defined(OSD_WINDOWS) -#include "emu.h" +Dance mats usually have this layout: +BK ST +B U A +L R +Y D X + +This layout seems somewhat unusual: +BK ST +A U B +L R +X D Y + +This layout is also available but rare: +BK ST +A U B +L R +Y D X + + +Drum kits also have multiple layouts. + +Rock Band: +B Y X A + LB + +Guitar Hero: + Y RB +A X B + LB + + +Rock Band keyboards use axes as bit fields: + +LT 7 C +LT 6 C# +LT 5 D +LT 4 D# +LT 3 E +LT 2 F +LT 1 F# +LT 0 G +RT 7 G# +RT 6 A +RT 5 A# +RT 4 B +RT 3 C +RT 2 C# +RT 1 D +RT 0 D# +LSX 7 E +LSX 6 F +LSX 5 F# +LSX 4 G +LSX 3 G# +LSX 2 A +LSX 1 A# +LSX 0 B +LSX 15 C + +*/ + +#include "modules/osdmodule.h" + +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include "input_xinput.h" -// standard windows headers -#include <windows.h> +#include "assignmenthelper.h" + +#include "interface/inputseq.h" +#include "modules/lib/osdobj_common.h" + +// emu +#include "inpttype.h" + +// lib/util +#include "util/coretmpl.h" + +#include "eminline.h" + +#include <algorithm> +#include <cstdint> +#include <iterator> +#include <string> +#include <tuple> +#include <utility> #define XINPUT_LIBRARIES { "xinput1_4.dll", "xinput9_1_0.dll" } -#define XINPUT_AXIS_MINVALUE -32767 -#define XINPUT_AXIS_MAXVALUE 32767 +#define XINPUT_AXIS_MINVALUE (-32'767) +#define XINPUT_AXIS_MAXVALUE (32'767) + +namespace osd { namespace { -// default axis names -const char *const xinput_axis_name[] = +using util::BIT; + + +char const *const AXIS_NAMES_GAMEPAD[]{ + "LSX", + "LSY", + "RSX", + "RSY", + "LT", + "RT" }; + +char const *const AXIS_NAMES_WHEEL[]{ + "Wheel", + "LSY", + "RSX", + "RSY", + "Brake", + "Accelerator" }; + +char const *const AXIS_NAMES_GUITAR[]{ + "LSX", + "LSY", + "Whammy Bar", + "Orientation", + "Pickup Selector", + "RT" }; + +input_item_id const AXIS_IDS_GAMEPAD[]{ + ITEM_ID_XAXIS, + ITEM_ID_YAXIS, + ITEM_ID_ZAXIS, + ITEM_ID_RZAXIS, + ITEM_ID_SLIDER1, + ITEM_ID_SLIDER2 }; + +char const *const HAT_NAMES_GAMEPAD[]{ + "D-pad Up", + "D-pad Down", + "D-pad Left", + "D-pad Right", + "POV Hat Up", + "POV Hat Down", + "POV Hat Left", + "POV Hat Right" }; + +char const *const HAT_NAMES_ARCADE_STICK[]{ + "Joystick Up", + "Joystick Down", + "Joystick Left", + "Joystick Right", + nullptr, + nullptr, + nullptr, + nullptr }; + +char const *const HAT_NAMES_GUITAR[]{ + "Strum/D-pad Up", + "Strum/D-pad Down", + "D-pad Left", + "D-pad Right" }; + +char const *const BUTTON_NAMES_GAMEPAD[]{ + "A", + "B", + "X", + "Y", + "LT", + "RT", + "LB", + "RB", + "LSB", + "RSB" }; + +char const *const BUTTON_NAMES_FLIGHT_STICK[]{ + "A", + "B", + "X", + "Y", + "LB", + "RB", + "LSB", + "RSB" }; + +char const *const BUTTON_NAMES_GUITAR[]{ + "Fret 1", + "Fret 2", + "Fret 3", + "Fret 4", + "Fret 5", + "Fret Modifier", + "RB", + "RSB" }; + +char const *const BUTTON_NAMES_DRUMKIT[]{ + "Green", // floor tom + "Red", // snare + "Blue", // low tom + "Yellow", // Rock Band high tom, Guitar Hero hi-hat + "Orange", // Guitar Hero crash cymbal + "Bass Drum", + "LSB", + "RSB" }; + +char const *const BUTTON_NAMES_KEYBOARD[]{ + "A", + "B", + "X", + "Y", + "LB", + "RB", + "LSB", + "RSB" }; + + + +//============================================================ +// base class for XInput controller handlers +//============================================================ + +class xinput_device_base : public device_info, protected joystick_assignment_helper { - "LSX", - "LSY", - "RSX", - "RSY" +protected: + xinput_device_base( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); + + // capabilities + BYTE device_type() const { return m_capabilities.Type; } + BYTE device_subtype() const { return m_capabilities.SubType; } + bool has_button(WORD mask) const { return (m_capabilities.Gamepad.wButtons & mask) != 0; } + bool has_trigger_left() const { return m_capabilities.Gamepad.bLeftTrigger != 0; } + bool has_trigger_right() const { return m_capabilities.Gamepad.bRightTrigger != 0; } + bool has_thumb_left_x() const { return m_capabilities.Gamepad.sThumbLX != 0; } + bool has_thumb_left_y() const { return m_capabilities.Gamepad.sThumbLY != 0; } + bool has_thumb_right_x() const { return m_capabilities.Gamepad.sThumbRX != 0; } + bool has_thumb_right_y() const { return m_capabilities.Gamepad.sThumbRY != 0; } + + // device state + WORD buttons() const { return m_xinput_state.Gamepad.wButtons; } + BYTE trigger_left() const { return m_xinput_state.Gamepad.bLeftTrigger; } + BYTE trigger_right() const { return m_xinput_state.Gamepad.bRightTrigger; } + SHORT thumb_left_x() const { return m_xinput_state.Gamepad.sThumbLX; } + SHORT thumb_left_y() const { return m_xinput_state.Gamepad.sThumbLY; } + SHORT thumb_right_x() const { return m_xinput_state.Gamepad.sThumbRX; } + SHORT thumb_right_y() const { return m_xinput_state.Gamepad.sThumbRY; } + + bool read_state(); + bool is_reset() const { return m_reset; } + void set_reset() { m_reset = true; } + +protected: + template <unsigned M, unsigned N> + static bool assign_ui_button( + input_device::assignment_vector &assignments, + ioport_type type, + unsigned preferred, + input_item_id (&switch_ids)[M], + unsigned const (&numbered_buttons)[N], + unsigned button_count); + + template <unsigned M, unsigned N> + static void assign_ui_actions( + input_device::assignment_vector &assignments, + unsigned preferred_back, + unsigned preferred_clear, + unsigned preferred_help, + unsigned start, + unsigned back, + input_item_id (&switch_ids)[M], + unsigned const (&numbered_buttons)[N], + unsigned button_count); + +private: + bool probe_extended_type(); + + u32 const m_player_index; + XINPUT_CAPABILITIES m_capabilities; + XINPUT_STATE m_xinput_state; + bool m_reset; + + xinput_api_helper const &m_xinput_helper; }; -const input_item_id xinput_axis_ids[] = + +xinput_device_base::xinput_device_base( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + device_info(std::move(name), std::move(id), module), + m_player_index(player), + m_capabilities(caps), + m_xinput_state{ 0 }, + m_reset(true), + m_xinput_helper(helper) { - ITEM_ID_XAXIS, - ITEM_ID_YAXIS, - ITEM_ID_RXAXIS, - ITEM_ID_RYAXIS -}; + // get friendly names for controller type and subtype + char const *type_name = "unsupported"; + char const *subtype_name = "unsupported"; + switch (m_capabilities.Type) + { + case XINPUT_DEVTYPE_GAMEPAD: + type_name = "game controller"; + switch (m_capabilities.SubType) + { + case 0x00: // XINPUT_DEVSUBTYPE_UNKNOWN: work around MinGW header issues + subtype_name = "unknown"; + break; + case XINPUT_DEVSUBTYPE_GAMEPAD: + subtype_name = "gamepad"; + break; + case XINPUT_DEVSUBTYPE_WHEEL: + subtype_name = "wheel"; + break; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + subtype_name = "arcade stick"; + break; + case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK: work around MinGW header issues + subtype_name = "flight stick"; + break; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + subtype_name = "dance pad"; + break; + case XINPUT_DEVSUBTYPE_GUITAR: + subtype_name = "guitar"; + break; + case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: + subtype_name = "alternate guitar"; + break; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + subtype_name = "drum kit"; + break; + case XINPUT_DEVSUBTYPE_GUITAR_BASS: + subtype_name = "bass guitar"; + break; + case XINPUT_DEVSUBTYPE_ARCADE_PAD: + subtype_name = "arcade pad"; + break; + } + break; + } -const USHORT xinput_pov_dir[] = { - XINPUT_GAMEPAD_DPAD_UP, - XINPUT_GAMEPAD_DPAD_DOWN, - XINPUT_GAMEPAD_DPAD_LEFT, - XINPUT_GAMEPAD_DPAD_RIGHT -}; + // detect invalid axis resolutions + bool const ltcap_bad = m_capabilities.Gamepad.bLeftTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bLeftTrigger << 24); + bool const rtcap_bad = m_capabilities.Gamepad.bRightTrigger && count_leading_zeros_32(m_capabilities.Gamepad.bRightTrigger << 24); + bool const lsxcap_bad = m_capabilities.Gamepad.sThumbLX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLX << 16); + bool const lsycap_bad = m_capabilities.Gamepad.sThumbLY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbLY << 16); + bool const rsxcap_bad = m_capabilities.Gamepad.sThumbRX && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRX << 16); + bool const rsycap_bad = m_capabilities.Gamepad.sThumbRY && count_leading_zeros_32(m_capabilities.Gamepad.sThumbRY << 16); -const char *const xinput_pov_names[] = { - "DPAD Up", - "DPAD Down", - "DPAD Left", - "DPAD Right" -}; + // log some diagnostic information + osd_printf_verbose( + "XInput: Configuring player %d type 0x%02X (%s) sub type 0x%02X (%s).\n", + m_player_index + 1, + m_capabilities.Type, + type_name, + m_capabilities.SubType, + subtype_name); + osd_printf_verbose( + "XInput: Switch capabilities A=%d B=%d X=%d Y=%d LB=%d RB=%d LSB=%d RSB=%d Start=%d Back=%d Up=%d Down=%d Left=%d Right=%d.\n", + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1 : 0, + (m_capabilities.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1 : 0); + osd_printf_verbose( + "XInput: Axis capabilities LT=0x%02X (%d-bit%s) RT=0x%02X (%d-bit%s) LSX=0x%04X (%d-bit%s) LSY=0x%04X (%d-bit%s) RSX=0x%04X (%d-bit%s) RSY=0x%04X (%d-bit%s).\n", + m_capabilities.Gamepad.bLeftTrigger, + count_leading_ones_32(u32(m_capabilities.Gamepad.bLeftTrigger) << 24), + ltcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.bRightTrigger, + count_leading_ones_32(u32(m_capabilities.Gamepad.bRightTrigger) << 24), + rtcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbLX, + count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbLX)) << 16), + lsxcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbLY, + count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbLY)) << 16), + lsycap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbRX, + count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbRX)) << 16), + rsxcap_bad ? ", invalid" : "", + m_capabilities.Gamepad.sThumbRY, + count_leading_ones_32(u32(u16(m_capabilities.Gamepad.sThumbRY)) << 16), + rsycap_bad ? ", invalid" : ""); + + // ignore capabilities if invalid + if (!probe_extended_type()) + { + bool ignore_caps = false; + if (ltcap_bad || rtcap_bad || lsxcap_bad || lsycap_bad || rsxcap_bad || rsycap_bad) + { + // Retro-Bit Sega Saturn Control Pad reports garbage for axis resolutions and absence of several buttons + osd_printf_verbose("XInput: Ignoring invalid capabilities (invalid axis resolution).\n"); + ignore_caps = true; + } + else if (!m_capabilities.Gamepad.wButtons && !m_capabilities.Gamepad.bLeftTrigger && !m_capabilities.Gamepad.bRightTrigger && !m_capabilities.Gamepad.sThumbLX && !m_capabilities.Gamepad.sThumbLY && !m_capabilities.Gamepad.sThumbRX && !m_capabilities.Gamepad.sThumbRY) + { + // 8BitDo SN30 Pro V1 reports no controls at all, which would be completely useless + osd_printf_verbose("XInput: Ignoring invalid capabilities (no controls reported).\n"); + ignore_caps = true; + } + if (ignore_caps) + { + m_capabilities.Gamepad.wButtons = 0xf3ff; + m_capabilities.Gamepad.bLeftTrigger = 0xff; + m_capabilities.Gamepad.bRightTrigger = 0xff; + m_capabilities.Gamepad.sThumbLX = s16(u16(0xffc0)); + m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); + m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); + m_capabilities.Gamepad.sThumbRY = s16(u16(0xffc0)); + } + } +} -const USHORT xinput_buttons[] = { - XINPUT_GAMEPAD_A, - XINPUT_GAMEPAD_B, - XINPUT_GAMEPAD_X, - XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_BACK, - XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB, -}; -const char *const xinput_button_names[] = { - "A", - "B", - "X", - "Y", - "LB", - "RB", - "Start", - "Back", - "LS", - "RS" +bool xinput_device_base::read_state() +{ + // save previous packet number and try to read peripheral state + DWORD const prevpacket = m_xinput_state.dwPacketNumber; + HRESULT const result = m_xinput_helper.xinput_get_state(m_player_index, &m_xinput_state); + + // only update if it succeeded and the packed number changed + if (FAILED(result)) + { + return false; + } + else if (m_reset) + { + m_reset = false; + return true; + } + else + { + return prevpacket != m_xinput_state.dwPacketNumber; + } +} + + +template <unsigned M, unsigned N> +bool xinput_device_base::assign_ui_button( + input_device::assignment_vector &assignments, + ioport_type type, + unsigned preferred, + input_item_id (&switch_ids)[M], + unsigned const (&numbered_buttons)[N], + unsigned button_count) +{ + assert(N >= button_count); + + // use preferred button if available + if (add_button_assignment(assignments, type, { switch_ids[preferred] })) + { + switch_ids[preferred] = ITEM_ID_INVALID; + return true; + } + + // otherwise find next available button + for (unsigned i = 0; button_count > i; ++i) + { + if (add_button_assignment(assignments, type, { switch_ids[numbered_buttons[i]] })) + { + switch_ids[numbered_buttons[i]] = ITEM_ID_INVALID; + return true; + } + } + + // didn't find a suitable button + return false; +} + + +template <unsigned M, unsigned N> +void xinput_device_base::assign_ui_actions( + input_device::assignment_vector &assignments, + unsigned preferred_back, + unsigned preferred_clear, + unsigned preferred_help, + unsigned start, + unsigned back, + input_item_id (&switch_ids)[M], + unsigned const (&numbered_buttons)[N], + unsigned button_count) +{ + // the first button is always UI select if present, or we can fall back to start + if (1U <= button_count) + { + add_button_assignment(assignments, IPT_UI_SELECT, { switch_ids[numbered_buttons[0]] }); + switch_ids[numbered_buttons[0]] = ITEM_ID_INVALID; + } + else if (add_button_assignment(assignments, IPT_UI_SELECT, { switch_ids[start] })) + { + switch_ids[start] = ITEM_ID_INVALID; + } + + // UI clear is usually X + assign_ui_button( + assignments, + IPT_UI_CLEAR, + preferred_clear, + switch_ids, + numbered_buttons, + button_count); + + // UI back can fall back from B to the back button + bool const assigned_back = assign_ui_button( + assignments, + IPT_UI_BACK, + preferred_back, + switch_ids, + numbered_buttons, + button_count); + if (!assigned_back) + { + if (add_button_assignment(assignments, IPT_UI_BACK, { switch_ids[back] })) + switch_ids[back] = ITEM_ID_INVALID; + } + + // help takes Y if present + assign_ui_button( + assignments, + IPT_UI_HELP, + preferred_help, + switch_ids, + numbered_buttons, + button_count); +} + + +bool xinput_device_base::probe_extended_type() +{ + switch (m_capabilities.Gamepad.sThumbLX) + { + case 0x1430: + switch (m_capabilities.Gamepad.sThumbLY) + { + case 0x0705: + osd_printf_verbose("XInput: Detected Band Hero guitar controller.\n"); + m_capabilities.Gamepad.sThumbLX = s16(u16(0xffc0)); // neck slider + m_capabilities.Gamepad.sThumbLY = 0; + m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); // whammy bar + return true; + case 0x0805: + osd_printf_verbose("XInput: Detected Band Hero drum kit controller.\n"); + m_capabilities.Gamepad.sThumbLX = 0; + m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); // green/red velocity + m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); // blue/yellow velocity + return true; + case 0x1705: + osd_printf_verbose("XInput: Detected DJ Hero turntable controller.\n"); + m_capabilities.Gamepad.sThumbLX = 0; + m_capabilities.Gamepad.sThumbLY = s16(u16(0xffc0)); // turntable + m_capabilities.Gamepad.sThumbRX = s16(u16(0xffc0)); // effects dial + return true; + } + break; + case 0x1bad: + switch (m_capabilities.Gamepad.sThumbLY) + { + case 0x1330: + osd_printf_verbose("XInput: Detected Rock Band keyboard controller.\n"); + m_capabilities.Gamepad.sThumbLX = 0; // keys, velocity + m_capabilities.Gamepad.sThumbLY = 0; // not present? + m_capabilities.Gamepad.sThumbRX = 0; // not present? + return true; + } + break; + } + return false; +} + + + +//============================================================ +// general XInput controller handler +//============================================================ + +class xinput_joystick_device : public xinput_device_base +{ +public: + xinput_joystick_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); + + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_A, // button bits + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_LB, + SWITCH_RB, + SWITCH_LSB, + SWITCH_RSB, + SWITCH_START, + SWITCH_BACK, + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_LT, // for arcade stick/pad with LT/RT buttons + SWITCH_RT, + + SWITCH_TOTAL + }; + + enum + { + AXIS_LT, // half-axes for triggers + AXIS_RT, + + AXIS_LSX, // full-precision axes + AXIS_LSY, + AXIS_RSX, + AXIS_RSY, + + AXIS_TOTAL + }; + + static bool assign_pedal( + input_device::assignment_vector &assignments, + bool fallback_shoulder, + ioport_type type, + input_item_id preferred_axis, + input_item_id fallback_axis1, + input_item_id fallback_axis2, + input_item_modifier fallback_axis_modifier, + input_item_id trigger_button, + input_item_id shoulder_button, + input_item_id numbered_button); + + u8 m_switches[SWITCH_TOTAL]; + s32 m_axes[AXIS_TOTAL]; }; +xinput_joystick_device::xinput_joystick_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + xinput_device_base(std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_joystick_device::poll(bool relative_reset) +{ + // poll the device first, and skip if nothing changed + if (!read_state()) + return; + + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; + + // translate the triggers onto the negative side of the axes + m_axes[AXIS_LT] = -normalize_absolute_axis(trigger_left(), -255, 255); + m_axes[AXIS_RT] = -normalize_absolute_axis(trigger_right(), -255, 255); + + // translate full-precision axes - Y direction is opposite to what MAME uses + m_axes[AXIS_LSX] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_LSY] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_RSX] = normalize_absolute_axis(thumb_right_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_RSY] = normalize_absolute_axis(-thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + + // translate LT/RT switches for arcade sticks/pads + m_switches[SWITCH_LT] = (0x80 <= trigger_left()) ? 0xff : 0x00; + m_switches[SWITCH_RT] = (0x80 <= trigger_right()) ? 0xff : 0x00; +} + + +void xinput_joystick_device::reset() +{ + set_reset(); + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_joystick_device::configure(input_device &device) +{ + // TODO: proper support for dance mat controllers + + // default characteristics for a gamepad + bool button_diamond = true; + bool pedal_fallback_shoulder = true; + bool lt_rt_button = false; + char const *const *axis_names = AXIS_NAMES_GAMEPAD; + input_item_id const *preferred_axis_ids = AXIS_IDS_GAMEPAD; + char const *const *hat_names = HAT_NAMES_GAMEPAD; + char const *const *button_names = BUTTON_NAMES_GAMEPAD; + + // consider the device type to decide how to map controls + switch (device_type()) + { + case XINPUT_DEVTYPE_GAMEPAD: + switch (device_subtype()) + { + case XINPUT_DEVSUBTYPE_WHEEL: + pedal_fallback_shoulder = false; + axis_names = AXIS_NAMES_WHEEL; + break; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + button_diamond = false; + lt_rt_button = true; + hat_names = HAT_NAMES_ARCADE_STICK; + break; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + // TODO: proper support + button_diamond = false; + break; + case XINPUT_DEVSUBTYPE_ARCADE_PAD: + button_diamond = false; + lt_rt_button = true; + break; + } + break; + } + + // track item IDs for setting up default assignments + input_device::assignment_vector assignments; + input_item_id axis_ids[AXIS_TOTAL]; + input_item_id switch_ids[SWITCH_TOTAL]; + std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID); + + // add bidirectional axes + bool const axis_caps[]{ + has_thumb_left_x(), + has_thumb_left_y(), + has_thumb_right_x(), + has_thumb_right_y() }; + for (unsigned i = 0; std::size(axis_caps) > i; ++i) + { + if (axis_caps[i]) + { + axis_ids[AXIS_LSX + i] = device.add_item( + axis_names[i], + std::string_view(), + preferred_axis_ids[i], + generic_axis_get_state<s32>, + &m_axes[AXIS_LSX + i]); + } + else + { + axis_ids[AXIS_LSX + i] = ITEM_ID_INVALID; + } + } + + // add hats + bool const hat_caps[]{ + has_button(XINPUT_GAMEPAD_DPAD_UP), + has_button(XINPUT_GAMEPAD_DPAD_DOWN), + has_button(XINPUT_GAMEPAD_DPAD_LEFT), + has_button(XINPUT_GAMEPAD_DPAD_RIGHT) }; + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) + { + if (hat_caps[i]) + { + switch_ids[SWITCH_DPAD_UP + i] = device.add_item( + hat_names[i], + std::string_view(), + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<u8>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } + + // add buttons + std::pair<unsigned, bool> const button_caps[]{ + { SWITCH_A, has_button(XINPUT_GAMEPAD_A) }, + { SWITCH_B, has_button(XINPUT_GAMEPAD_B) }, + { SWITCH_X, has_button(XINPUT_GAMEPAD_X) }, + { SWITCH_Y, has_button(XINPUT_GAMEPAD_Y) }, + { SWITCH_LT, lt_rt_button && has_trigger_left() }, + { SWITCH_RT, lt_rt_button && has_trigger_right() }, + { SWITCH_LB, has_button(XINPUT_GAMEPAD_LEFT_SHOULDER) }, + { SWITCH_RB, has_button(XINPUT_GAMEPAD_RIGHT_SHOULDER) }, + { SWITCH_LSB, has_button(XINPUT_GAMEPAD_LEFT_THUMB) }, + { SWITCH_RSB, has_button(XINPUT_GAMEPAD_RIGHT_THUMB) } }; + input_item_id button_id = ITEM_ID_BUTTON1; + unsigned button_count = 0; + unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1]; + for (unsigned i = 0; std::size(button_caps) > i; ++i) + { + auto const [offset, supported] = button_caps[i]; + if (supported) + { + switch_ids[offset] = device.add_item( + button_names[i], + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[offset]); + numbered_buttons[button_count] = offset; + + // use these for automatically numbered buttons + assignments.emplace_back( + ioport_type(IPT_BUTTON1 + button_count++), + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[offset]))); + } + } + + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + switch_ids[SWITCH_START] = device.add_item( + "Start", + std::string_view(), + ITEM_ID_START, + generic_button_get_state<u8>, + &m_switches[SWITCH_START]); + add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] }); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + switch_ids[SWITCH_BACK] = device.add_item( + "Back", + std::string_view(), + ITEM_ID_SELECT, + generic_button_get_state<u8>, + &m_switches[SWITCH_BACK]); + add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] }); + } + + // add triggers/pedals + if (!lt_rt_button) + { + for (unsigned i = 0; (AXIS_RT - AXIS_LT) >= i; ++i) + { + if (i ? has_trigger_right() : has_trigger_left()) + { + axis_ids[AXIS_LT + i] = device.add_item( + axis_names[std::size(axis_caps) + i], + std::string_view(), + preferred_axis_ids[std::size(axis_caps) + i], + generic_axis_get_state<s32>, + &m_axes[AXIS_LT + i]); + } + else + { + axis_ids[AXIS_LT + i] = ITEM_ID_INVALID; + } + } + } + + // try to get a "complete" joystick for primary movement controls + input_item_id directional_axes[2][2]; + choose_primary_stick( + directional_axes, + axis_ids[AXIS_LSX], + axis_ids[AXIS_LSY], + axis_ids[AXIS_RSX], + axis_ids[AXIS_RSY]); + + // now set up controls using the primary joystick + add_directional_assignments( + assignments, + directional_axes[0][0], + directional_axes[0][1], + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN]); + + // assign a secondary stick axis to joystick Z if available + bool const stick_z = add_assignment( + assignments, + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NONE, + { directional_axes[1][1], directional_axes[1][0] }); + if (!stick_z) + { + // if both triggers are present, combine them, or failing that, fall back to a pair of buttons + if ((ITEM_ID_INVALID != axis_ids[AXIS_LT]) && (ITEM_ID_INVALID != axis_ids[AXIS_RT])) + { + assignments.emplace_back( + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + input_seq( + make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axis_ids[AXIS_LT]), + make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_REVERSE, axis_ids[AXIS_RT]))); + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, switch_ids[SWITCH_LB], switch_ids[SWITCH_RB])) + { + // took shoulder buttons + } + else if (add_axis_inc_dec_assignment(assignments, IPT_AD_STICK_Z, switch_ids[SWITCH_LT], switch_ids[SWITCH_RT])) + { + // took trigger buttons + } + } + + // prefer trigger axes for pedals, otherwise take half axes and buttons + unsigned pedal_button = 0; + bool const pedal1_numbered_button = assign_pedal( + assignments, + pedal_fallback_shoulder, + IPT_PEDAL, + axis_ids[AXIS_RT], + directional_axes[1][1], + directional_axes[0][1], + ITEM_MODIFIER_NEG, + switch_ids[SWITCH_RT], + switch_ids[SWITCH_RB], + (pedal_button < button_count) + ? switch_ids[numbered_buttons[pedal_button]] + : ITEM_ID_INVALID); + if (pedal1_numbered_button) + ++pedal_button; + bool const pedal2_numbered_button = assign_pedal( + assignments, + pedal_fallback_shoulder, + IPT_PEDAL2, + axis_ids[AXIS_LT], + directional_axes[1][1], + directional_axes[0][1], + ITEM_MODIFIER_POS, + switch_ids[SWITCH_LT], + switch_ids[SWITCH_LB], + (pedal_button < button_count) + ? switch_ids[numbered_buttons[pedal_button]] + : ITEM_ID_INVALID); + if (pedal2_numbered_button) + ++pedal_button; + if (pedal_button < button_count) + { + input_item_id const pedal_button_id = switch_ids[numbered_buttons[pedal_button]]; + assignments.emplace_back( + IPT_PEDAL3, + SEQ_TYPE_INCREMENT, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, pedal_button_id))); + } + + // potentially use thumb sticks and/or D-pad and A/B/X/Y diamond for twin sticks + add_twin_stick_assignments( + assignments, + axis_ids[AXIS_LSX], + axis_ids[AXIS_LSY], + axis_ids[AXIS_RSX], + axis_ids[AXIS_RSY], + button_diamond ? switch_ids[SWITCH_DPAD_LEFT] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_DPAD_RIGHT] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_DPAD_UP] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_DPAD_DOWN] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_X] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_B] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_Y] : ITEM_ID_INVALID, + button_diamond ? switch_ids[SWITCH_A] : ITEM_ID_INVALID); + + // assign UI select/back/clear/help + assign_ui_actions( + assignments, + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_START, + SWITCH_BACK, + switch_ids, + numbered_buttons, + button_count); + + // try to get a matching pair of buttons for previous/next group + if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_LT], switch_ids[SWITCH_RT])) + { + // took digital triggers + } + else if (consume_trigger_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_LT], axis_ids[AXIS_RT])) + { + // took analog triggers + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, directional_axes[1][1])) + { + // took secondary Y + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, directional_axes[1][0])) + { + // took secondary X + } + + // try to assign secondary stick to page up/down + consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, directional_axes[1][1]); + + // put focus previous/next on the shoulder buttons if available - this can be overloaded with zoom + if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_LB], switch_ids[SWITCH_RB])) + { + // took shoulder buttons + } + else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, directional_axes[1][0])) + { + // took secondary X + } + + // put zoom on the secondary stick if available, or fall back to shoulder buttons + if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, directional_axes[1][0])) + { + // took secondary X + if (axis_ids[AXIS_LSX] == directional_axes[1][0]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { switch_ids[SWITCH_LSB] }); + else if (axis_ids[AXIS_RSX] == directional_axes[1][0]) + add_button_assignment(assignments, IPT_UI_ZOOM_DEFAULT, { switch_ids[SWITCH_RSB] }); + directional_axes[1][0] = ITEM_ID_INVALID; + } + else if (consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_LB], switch_ids[SWITCH_RB])) + { + // took shoulder buttons + } + + // set default assignments + device.set_default_assignments(std::move(assignments)); +} + + +bool xinput_joystick_device::assign_pedal( + input_device::assignment_vector &assignments, + bool fallback_shoulder, + ioport_type type, + input_item_id preferred_axis, + input_item_id fallback_axis1, + input_item_id fallback_axis2, + input_item_modifier fallback_axis_modifier, + input_item_id trigger_button, + input_item_id shoulder_button, + input_item_id numbered_button) +{ + // first try the preferred trigger/pedal axis + if (ITEM_ID_INVALID != preferred_axis) + { + assignments.emplace_back( + type, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, preferred_axis))); + return false; + } + + // try adding half a joystick axis + add_assignment( + assignments, + type, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + fallback_axis_modifier, + { fallback_axis1, fallback_axis2 }); + + // try a trigger button + if (ITEM_ID_INVALID != trigger_button) + { + assignments.emplace_back( + type, + SEQ_TYPE_INCREMENT, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, trigger_button))); + return false; + } + + // try a shoulder button if appropriate + if (fallback_shoulder && (ITEM_ID_INVALID != shoulder_button)) + { + assignments.emplace_back( + type, + SEQ_TYPE_INCREMENT, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, shoulder_button))); + return false; + } + + // if no numbered button, nothing can be done + if (ITEM_ID_INVALID == numbered_button) + return false; + + // last resort + assignments.emplace_back( + type, + SEQ_TYPE_INCREMENT, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, numbered_button))); + return true; +} + + + //============================================================ -// xinput_joystick_module +// XInput flight stick handler //============================================================ -class xinput_joystick_module : public wininput_module +class xinput_flight_stick_device : public xinput_device_base { public: - xinput_joystick_module() : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "xinput") + xinput_flight_stick_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); + + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_A, // button bits + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_LB, + SWITCH_RB, + SWITCH_LSB, + SWITCH_RSB, + SWITCH_START, + SWITCH_BACK, + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_HAT_UP, // for POV hat as right stick + SWITCH_HAT_DOWN, + SWITCH_HAT_LEFT, + SWITCH_HAT_RIGHT, + + SWITCH_TOTAL + }; + + enum + { + AXIS_RUDDER, // LT/RT mapped as bidirectional axes + AXIS_THROTTLE, + + AXIS_X, // full-precision axes + AXIS_Y, + + AXIS_TOTAL + }; + + u8 m_switches[SWITCH_TOTAL]; + s32 m_axes[AXIS_TOTAL]; +}; + + +xinput_flight_stick_device::xinput_flight_stick_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + xinput_device_base(std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_flight_stick_device::poll(bool relative_reset) +{ + // poll the device first, and skip if nothing changed + if (!read_state()) + return; + + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; + + // translate rudder and throttle + m_axes[AXIS_RUDDER] = normalize_absolute_axis(trigger_left(), 0, 255); + m_axes[AXIS_THROTTLE] = normalize_absolute_axis(trigger_right(), 0, 255); + + // translate full-precision axes - Y direction is opposite to what MAME uses + m_axes[AXIS_X] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_Y] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + + // translate right stick as POV hat + m_switches[SWITCH_HAT_UP] = (16'384 <= thumb_right_y()) ? 0xff : 0x00; + m_switches[SWITCH_HAT_DOWN] = (-16'384 >= thumb_right_y()) ? 0xff : 0x00; + m_switches[SWITCH_HAT_LEFT] = (-16'384 >= thumb_right_x()) ? 0xff : 0x00; + m_switches[SWITCH_HAT_RIGHT] = (16'384 <= thumb_right_x()) ? 0xff : 0x00; +} + + +void xinput_flight_stick_device::reset() +{ + set_reset(); + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_flight_stick_device::configure(input_device &device) +{ + // track item IDs for setting up default assignments + input_device::assignment_vector assignments; + input_item_id switch_ids[SWITCH_TOTAL]; + std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID); + + // add bidirectional axes + std::tuple<input_item_id, char const *, bool> const axis_caps[]{ + { ITEM_ID_RZAXIS, "Rudder", has_trigger_left() }, + { ITEM_ID_ZAXIS, "Throttle", has_trigger_right() }, + { ITEM_ID_XAXIS, "Joystick X", has_thumb_left_x() }, + { ITEM_ID_YAXIS, "Joystick Y", has_thumb_left_y() } }; + input_item_id axis_ids[AXIS_TOTAL]; + for (unsigned i = 0; AXIS_TOTAL > i; ++i) { + auto const [id, name, supported] = axis_caps[i]; + if (supported) + { + axis_ids[i] = device.add_item( + name, + std::string_view(), + id, + generic_axis_get_state<s32>, + &m_axes[i]); + } + else + { + axis_ids[i] = ITEM_ID_INVALID; + } } - int init(const osd_options &options) override + // add hats + bool const hat_caps[]{ + has_button(XINPUT_GAMEPAD_DPAD_UP), + has_button(XINPUT_GAMEPAD_DPAD_DOWN), + has_button(XINPUT_GAMEPAD_DPAD_LEFT), + has_button(XINPUT_GAMEPAD_DPAD_RIGHT), + has_thumb_right_x(), + has_thumb_right_x(), + has_thumb_right_y(), + has_thumb_right_y() }; + for (unsigned i = 0; (SWITCH_HAT_RIGHT - SWITCH_DPAD_UP) >= i; ++i) { - // Call the base - int status = wininput_module::init(options); - if (status != 0) - return status; + if (hat_caps[i]) + { + switch_ids[SWITCH_DPAD_UP + i] = device.add_item( + HAT_NAMES_GAMEPAD[i], + std::string_view(), + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<u8>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } - // Create and initialize our helper - m_xinput_helper = std::make_shared<xinput_api_helper>(); - status = m_xinput_helper->initialize(); - if (status != 0) + // add buttons + input_item_id button_id = ITEM_ID_BUTTON1; + unsigned button_count = 0; + unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1]; + for (unsigned i = 0; (SWITCH_RSB - SWITCH_A) >= i; ++i) + { + if (has_button(SWITCH_BITS[i])) { - osd_printf_error("xinput_joystick_module failed to get XInput interface! Error: %u\n", static_cast<unsigned int>(status)); - return -1; + switch_ids[SWITCH_A + i] = device.add_item( + BUTTON_NAMES_FLIGHT_STICK[i], + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_A + i]); + numbered_buttons[button_count] = SWITCH_A + i; + + // use these for automatically numbered buttons and pedals + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_A + i])); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq); + if (3 > button_count) + assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq); + ++button_count; } + } - return 0; + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + switch_ids[SWITCH_START] = device.add_item( + "Start", + std::string_view(), + ITEM_ID_START, + generic_button_get_state<u8>, + &m_switches[SWITCH_START]); + add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] }); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + switch_ids[SWITCH_BACK] = device.add_item( + "Back", + std::string_view(), + ITEM_ID_SELECT, + generic_button_get_state<u8>, + &m_switches[SWITCH_BACK]); + add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] }); } -protected: - virtual void input_init(running_machine &machine) override + // use throttle for joystick Z, or rudder if it isn't available + add_assignment( + assignments, + IPT_AD_STICK_Z, + SEQ_TYPE_STANDARD, + ITEM_CLASS_ABSOLUTE, + ITEM_MODIFIER_NONE, + { axis_ids[AXIS_THROTTLE], axis_ids[AXIS_RUDDER] }); + + // if throttle is available, use it for first two pedals, too + if (ITEM_ID_INVALID != axis_ids[AXIS_THROTTLE]) { - // Loop through each gamepad to determine if they are connected - for (UINT i = 0; i < XUSER_MAX_COUNT; i++) + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axis_ids[AXIS_THROTTLE]))); + assignments.emplace_back( + IPT_PEDAL2, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axis_ids[AXIS_THROTTLE]))); + } + + // find something to use for directional controls and navigation + bool const axis_missing = (ITEM_ID_INVALID == axis_ids[AXIS_X]) || (ITEM_ID_INVALID == axis_ids[AXIS_Y]); + bool const hat_complete = has_thumb_right_x() && has_thumb_right_y(); + if (axis_missing && hat_complete) + { + // X or Y missing - rely on POV hat + add_directional_assignments( + assignments, + axis_ids[AXIS_X], + axis_ids[AXIS_Y], + switch_ids[SWITCH_HAT_LEFT], + switch_ids[SWITCH_HAT_RIGHT], + switch_ids[SWITCH_HAT_UP], + switch_ids[SWITCH_HAT_DOWN]); + + // choose something for previous/next group + if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad up/down + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_RUDDER])) + { + // took rudder + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_THROTTLE])) + { + // took throttle + } + + // choose something for zoom and focus previous/next + if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_THROTTLE])) + { + if (!add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_RUDDER])) + add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_THROTTLE]); + } + else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_RUDDER])) + { + add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_RUDDER]); + } + else if (add_button_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + consume_button_pair(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]); + } + } + else + { + // only use stick for the primary directional controls + add_directional_assignments( + assignments, + axis_ids[AXIS_X], + axis_ids[AXIS_Y], + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID); + + // assign the POV hat differently depending on whether rudder and/or throttle are present + if ((ITEM_ID_INVALID != axis_ids[AXIS_RUDDER]) || (ITEM_ID_INVALID != axis_ids[AXIS_THROTTLE])) + { + // previous/next group + if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT])) + { + // took hat left/right + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_UP], switch_ids[SWITCH_HAT_DOWN])) + { + // took hat up/down + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad up/down + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_RUDDER])) + { + // took rudder + } + else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, axis_ids[AXIS_THROTTLE])) + { + // took throttle + } + + // page up/down + if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_HAT_UP], switch_ids[SWITCH_HAT_DOWN])) + { + // took hat up/down + } + else if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad up/down + } + else if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } + + // home/end + if (consume_button_pair(assignments, IPT_UI_HOME, IPT_UI_END, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad up/down + } + else if (consume_button_pair(assignments, IPT_UI_HOME, IPT_UI_END, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } + + // assign something for zoom - this can overlap with focus previous/next + if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_THROTTLE])) + { + // took throttle + } + else if (add_button_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } + else if (add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, axis_ids[AXIS_RUDDER])) + { + // took rudder + } + + // assign something for focus previous/next + if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_RUDDER])) + { + // took rudder + } + else if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } + else if (add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, axis_ids[AXIS_THROTTLE])) + { + // took throttle + } + } + else { - XINPUT_STATE state = {0}; + // previous/next group + if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_UP], switch_ids[SWITCH_HAT_DOWN])) + { + // took hat up/down + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad up/down + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT])) + { + // took hat left/right + } + else if (consume_button_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right + } - if (m_xinput_helper->xinput_get_state(i, &state) == ERROR_SUCCESS) + // try to choose something for focus previous/next + if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT])) { - // allocate and link in a new device - auto *devinfo = m_xinput_helper->create_xinput_device(machine, i, *this); - if (!devinfo) - continue; + // took hat left/right - use it for zoom as well + consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_HAT_LEFT], switch_ids[SWITCH_HAT_RIGHT]); + } + else if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right - use it for zoom as well + consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]); + } + else if (add_button_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad left/right - use it for zoom as well + consume_button_pair(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN]); + } - // Configure each gamepad to add buttons and Axes, etc. - devinfo->configure(); + // use D-pad for page up/down and home/end if it's still available + if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_UP], switch_ids[SWITCH_DPAD_DOWN])) + { + // took D-pad up/down + } + else if (consume_button_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT])) + { + // took D-pad left/right } + consume_button_pair(assignments, IPT_UI_HOME, IPT_UI_END, switch_ids[SWITCH_DPAD_LEFT], switch_ids[SWITCH_DPAD_RIGHT]); } } + // assign UI select/back/clear/help + assign_ui_actions( + assignments, + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_START, + SWITCH_BACK, + switch_ids, + numbered_buttons, + button_count); + + // set default assignments + device.set_default_assignments(std::move(assignments)); +} + + + +//============================================================ +// XInput guitar handler +//============================================================ + +class xinput_guitar_device : public xinput_device_base +{ +public: + xinput_guitar_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); + + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + private: - std::shared_ptr<xinput_api_helper> m_xinput_helper; + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_FRET1, + SWITCH_FRET2, + SWITCH_FRET3, + SWITCH_FRET4, + SWITCH_FRET5, + SWITCH_FRET_MOD, // indicates a "solo fret" is pressed, not a separate button + SWITCH_RB, // often reported present but no physical button + SWITCH_RSB, // usually reported absent + SWITCH_START, + SWITCH_BACK, // also used for "Star Power" in some games + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_TOTAL + }; + + enum + { + AXIS_SLIDER, // LSX, positive toward bridge + AXIS_LSY, // not used on guitar controllers? + AXIS_WHAMMY, // RSX, single-ended, neutral at negative extreme + + AXIS_ORIENT_NECK, // RSY + AXIS_ORIENT_BRIDGE, // LT, toward zero with frets up + AXIS_ORIENT_BODY, // RT, toward zero in right-handed orientation + + AXIS_PICKUP, // LT, positive extreme at one end + + AXIS_TOTAL + }; + + u8 m_switches[SWITCH_TOTAL]; + s32 m_axes[AXIS_TOTAL]; }; -} // anonymous namespace + +xinput_guitar_device::xinput_guitar_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + xinput_device_base(std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} -int xinput_api_helper::initialize() +void xinput_guitar_device::poll(bool relative_reset) { - m_xinput_dll = osd::dynamic_module::open(XINPUT_LIBRARIES); + // poll the device first, and skip if nothing changed + if (!read_state()) + return; - XInputGetState = m_xinput_dll->bind<xinput_get_state_fn>("XInputGetState"); - XInputGetCapabilities = m_xinput_dll->bind<xinput_get_caps_fn>("XInputGetCapabilities"); + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_FRET1 + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; - if (!XInputGetState || !XInputGetCapabilities) + // translate miscellaneous axes + m_axes[AXIS_SLIDER] = normalize_absolute_axis(thumb_left_x(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_LSY] = normalize_absolute_axis(-thumb_left_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_WHAMMY] = -normalize_absolute_axis(s32(thumb_right_x()) + 32'768, -65'535, 65'535); + + // translate orientation sensors + m_axes[AXIS_ORIENT_NECK] = normalize_absolute_axis(-thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + m_axes[AXIS_ORIENT_BRIDGE] = normalize_absolute_axis(trigger_left(), 0, 255); + m_axes[AXIS_ORIENT_BODY] = normalize_absolute_axis(trigger_right(), 0, 255); + + // translate pickup selector + m_axes[AXIS_PICKUP] = -normalize_absolute_axis(trigger_left(), -255, 255); +} + + +void xinput_guitar_device::reset() +{ + set_reset(); + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_guitar_device::configure(input_device &device) +{ + // TODO: does subtype 0x06 indicate digital neck orientation sensor or lack of three-axis accelerometer? + + // track item IDs for setting up default assignments + input_device::assignment_vector assignments; + input_item_id switch_ids[SWITCH_TOTAL]; + std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID); + + // add axes + std::tuple<input_item_id, char const *, bool> const axis_caps[]{ + { ITEM_ID_RXAXIS, "Neck Slider", has_thumb_left_x() }, + { ITEM_ID_RYAXIS, "LSY", has_thumb_left_y() }, + { ITEM_ID_SLIDER1, "Whammy Bar", has_thumb_right_x() }, + { ITEM_ID_YAXIS, "Neck Orientation", has_thumb_right_y() }, + { ITEM_ID_XAXIS, "Bridge Orientation", has_trigger_left() && has_trigger_right() }, + { ITEM_ID_ZAXIS, "Body Orientation", has_trigger_right() }, + { ITEM_ID_SLIDER2, "Pickup Selector", has_trigger_left() && !has_trigger_right() } }; + input_item_id axis_ids[AXIS_TOTAL]; + for (unsigned i = 0; AXIS_TOTAL > i; ++i) { - osd_printf_verbose("Could not find XInput. Please try to reinstall DirectX runtime package.\n"); - return -1; + auto const [item, name, supported] = axis_caps[i]; + if (supported) + { + axis_ids[i] = device.add_item( + name, + std::string_view(), + item, + generic_axis_get_state<s32>, + &m_axes[i]); + } + else + { + axis_ids[i] = ITEM_ID_INVALID; + } } - return 0; + // add hats + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) + { + if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i])) + { + switch_ids[SWITCH_DPAD_UP + i] = device.add_item( + HAT_NAMES_GUITAR[i], + std::string_view(), + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<u8>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } + + // add buttons + input_item_id button_id = ITEM_ID_BUTTON1; + unsigned button_count = 0; + unsigned numbered_buttons[SWITCH_FRET5 - SWITCH_FRET1 + 1]; + for (unsigned i = 0; (SWITCH_RSB - SWITCH_FRET1) >= i; ++i) + { + if (has_button(SWITCH_BITS[i])) + { + switch_ids[SWITCH_FRET1 + i] = device.add_item( + BUTTON_NAMES_GUITAR[i], + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_FRET1 + i]); + + // use fret buttons for automatically numbered buttons and pedals + if ((SWITCH_FRET5 - SWITCH_FRET1) >= i) + { + numbered_buttons[button_count] = SWITCH_FRET1 + i; + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_FRET1 + i])); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq); + if (((ITEM_ID_INVALID != axis_ids[AXIS_WHAMMY]) ? 2 : 3) > button_count) + { + ioport_type const first_pedal = (ITEM_ID_INVALID != axis_ids[AXIS_WHAMMY]) + ? IPT_PEDAL2 + : IPT_PEDAL; + assignments.emplace_back( + ioport_type(first_pedal + button_count), + SEQ_TYPE_INCREMENT, + seq); + } + ++button_count; + } + } + } + + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + switch_ids[SWITCH_START] = device.add_item( + "Start", + std::string_view(), + ITEM_ID_START, + generic_button_get_state<u8>, + &m_switches[SWITCH_START]); + add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] }); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + switch_ids[SWITCH_BACK] = device.add_item( + "Back", + std::string_view(), + ITEM_ID_SELECT, + generic_button_get_state<u8>, + &m_switches[SWITCH_BACK]); + add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] }); + } + + // use the D-pad for directional controls - accelerometers are an annoyance + add_directional_assignments( + assignments, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN]); + + // use the whammy bar for the first pedal and focus next if present + if (ITEM_ID_INVALID != axis_ids[AXIS_WHAMMY]) + { + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axis_ids[AXIS_WHAMMY]))); + assignments.emplace_back( + IPT_UI_FOCUS_NEXT, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axis_ids[AXIS_WHAMMY]))); + } + + // use the neck slider for a couple of things if it's present + if (ITEM_ID_INVALID != axis_ids[AXIS_SLIDER]) + { + input_seq const seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, axis_ids[AXIS_SLIDER])); + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_STANDARD, seq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, seq); + } + + // assign UI select/back/clear/help + assign_ui_actions( + assignments, + SWITCH_FRET2, + SWITCH_FRET3, + SWITCH_FRET4, + SWITCH_START, + SWITCH_BACK, + switch_ids, + numbered_buttons, + button_count); + + // set default assignments + device.set_default_assignments(std::move(assignments)); } + + //============================================================ -// create_xinput_device +// XInput drum kit handler //============================================================ -xinput_joystick_device * xinput_api_helper::create_xinput_device(running_machine &machine, UINT index, wininput_module &module) +class xinput_drumkit_device : public xinput_device_base { - XINPUT_CAPABILITIES caps = { 0 }; - if (FAILED(xinput_get_capabilities(index, 0, &caps))) +public: + xinput_drumkit_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); + + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = { - // If we can't get the capabilities skip this device - return nullptr; + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_GREEN, // button bits + SWITCH_RED, + SWITCH_BLUE, + SWITCH_YELLOW, + SWITCH_ORANGE, + SWITCH_BASS_DRUM, + SWITCH_LSB, + SWITCH_RSB, + SWITCH_START, + SWITCH_BACK, + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_TOTAL + }; + + enum + { + AXIS_GREEN, // LSY low + AXIS_RED, // LSY high + AXIS_BLUE, // RSX high + AXIS_YELLOW, // RSX low + AXIS_ORANGE, // RSY low + AXIS_BASS_DRUM, // RSY high + + AXIS_TOTAL + }; + + u8 m_switches[SWITCH_TOTAL]; + s32 m_axes[AXIS_TOTAL]; +}; + + +xinput_drumkit_device::xinput_drumkit_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + xinput_device_base(std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_drumkit_device::poll(bool relative_reset) +{ + // poll the device first, and skip if nothing changed + if (!read_state()) + return; + + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_GREEN + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; + + // translate axes + m_axes[AXIS_GREEN] = -normalize_absolute_axis(BIT(u16(thumb_left_y()), 0, 8), -255, 255); + m_axes[AXIS_RED] = -normalize_absolute_axis(BIT(u16(thumb_left_y()), 8, 8), -255, 255); + m_axes[AXIS_BLUE] = -normalize_absolute_axis(BIT(u16(thumb_right_x()), 8, 8), -255, 255); + m_axes[AXIS_YELLOW] = -normalize_absolute_axis(BIT(u16(thumb_right_x()), 0, 8), -255, 255); + m_axes[AXIS_ORANGE] = -normalize_absolute_axis(BIT(u16(thumb_right_y()), 0, 8), -255, 255); + m_axes[AXIS_BASS_DRUM] = -normalize_absolute_axis(BIT(u16(thumb_right_y()), 8, 8), -255, 255); +} + + +void xinput_drumkit_device::reset() +{ + set_reset(); + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_drumkit_device::configure(input_device &device) +{ + // track item IDs for setting up default assignments + input_device::assignment_vector assignments; + input_item_id switch_ids[SWITCH_TOTAL]; + std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID); + + // add axes + std::tuple<input_item_id, char const *, bool> const axis_caps[]{ + { ITEM_ID_XAXIS, "Green Velocity", has_thumb_left_y() }, + { ITEM_ID_YAXIS, "Red Velocity", has_thumb_left_y() }, + { ITEM_ID_ZAXIS, "Blue Velocity", has_thumb_right_x() }, + { ITEM_ID_RXAXIS, "Yellow Velocity", has_thumb_right_x() }, + { ITEM_ID_RYAXIS, "Orange Velocity", has_thumb_right_y() }, + { ITEM_ID_RZAXIS, "Bass Drum Velocity", has_thumb_right_y() } }; + for (unsigned i = 0; (AXIS_BASS_DRUM - AXIS_GREEN) >= i; ++i) + { + auto const [item, name, supported] = axis_caps[i]; + if (supported) + { + device.add_item( + name, + std::string_view(), + item, + generic_axis_get_state<s32>, + &m_axes[AXIS_GREEN + i]); + } } - char device_name[16]; - snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1); + // add hats + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) + { + if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i])) + { + switch_ids[SWITCH_DPAD_UP + i] = device.add_item( + HAT_NAMES_GAMEPAD[i], + std::string_view(), + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<u8>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } - // allocate the device object - auto &devinfo = module.devicelist().create_device<xinput_joystick_device>(machine, device_name, device_name, module, shared_from_this()); + // add buttons + unsigned button_count = 0; + unsigned numbered_buttons[SWITCH_RSB - SWITCH_GREEN + 1]; + for (unsigned i = 0; (SWITCH_RSB - SWITCH_GREEN) >= i; ++i) + { + if (has_button(SWITCH_BITS[i])) + { + switch_ids[SWITCH_GREEN + i] = device.add_item( + BUTTON_NAMES_DRUMKIT[i], + std::string_view(), + input_item_id(ITEM_ID_BUTTON1 + button_count), + generic_button_get_state<u8>, + &m_switches[SWITCH_GREEN + i]); + numbered_buttons[button_count] = SWITCH_GREEN + i; - // Set the player ID - devinfo.xinput_state.player_index = index; + // use these for automatically numbered buttons and pedals + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_GREEN + i])); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq); + if (3 > button_count) + assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq); + ++button_count; + } + } - // Assign the caps we captured earlier - devinfo.xinput_state.caps = caps; + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + switch_ids[SWITCH_START] = device.add_item( + "Start", + std::string_view(), + ITEM_ID_START, + generic_button_get_state<u8>, + &m_switches[SWITCH_START]); + add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] }); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + switch_ids[SWITCH_BACK] = device.add_item( + "Back", + std::string_view(), + ITEM_ID_SELECT, + generic_button_get_state<u8>, + &m_switches[SWITCH_BACK]); + add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] }); + } + + // use the D-pad for directional controls + add_directional_assignments( + assignments, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN]); + + // use the D-pad and A/B/X/Y diamond for twin sticks + add_twin_stick_assignments( + assignments, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN], + switch_ids[SWITCH_BLUE], + switch_ids[SWITCH_RED], + switch_ids[SWITCH_YELLOW], + switch_ids[SWITCH_GREEN]); - return &devinfo; + // assign UI select/back/clear/help + assign_ui_actions( + assignments, + SWITCH_RED, + SWITCH_BLUE, + SWITCH_YELLOW, + SWITCH_START, + SWITCH_BACK, + switch_ids, + numbered_buttons, + button_count); + + // use bass drum pedal for focus next if available to make the system selection menu usable + if (add_button_assignment(assignments, IPT_UI_FOCUS_NEXT, { switch_ids[SWITCH_BASS_DRUM] })) + switch_ids[SWITCH_BASS_DRUM] = ITEM_ID_INVALID; + + // set default assignments + device.set_default_assignments(std::move(assignments)); } + + //============================================================ -// xinput_joystick_device +// XInput turntable handler //============================================================ -xinput_joystick_device::xinput_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module, std::shared_ptr<xinput_api_helper> helper) : - device_info(machine, std::string(name), std::string(id), DEVICE_CLASS_JOYSTICK, module), - gamepad({ { 0 } }), - xinput_state({ 0 }), - m_xinput_helper(helper), - m_configured(false) +class xinput_turntable_device : public xinput_device_base { -} +public: + xinput_turntable_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); -void xinput_joystick_device::poll() + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_A, // button bits + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_LB, + SWITCH_RB, + SWITCH_LSB, + SWITCH_RSB, + SWITCH_START, + SWITCH_BACK, + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_GREEN, // RT bits + SWITCH_RED, + SWITCH_BLUE, + + SWITCH_TOTAL + }; + + enum + { + AXIS_TURNTABLE, // LSY + AXIS_EFFECT, // RSX + AXIS_CROSSFADE, // RSY + + AXIS_TOTAL + }; + + u8 m_switches[SWITCH_TOTAL]; + s32 m_axes[AXIS_TOTAL]; + u16 m_prev_effect; +}; + + +xinput_turntable_device::xinput_turntable_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + xinput_device_base(std::move(name), std::move(id), module, player, caps, helper), + m_prev_effect(0) { - if (!m_configured) - return; + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + +void xinput_turntable_device::poll(bool relative_reset) +{ // poll the device first - HRESULT result = m_xinput_helper->xinput_get_state(xinput_state.player_index, &xinput_state.xstate); + bool const was_reset = is_reset(); + if (read_state()) + { + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; - // If we can't poll the device, skip - if (FAILED(result)) - return; + // translate RT bits + for (unsigned i = 0; (SWITCH_BLUE - SWITCH_GREEN) >= i; ++i) + m_switches[SWITCH_GREEN + i] = BIT(trigger_right(), i) ? 0xff : 0x00; + + // translate axes + m_axes[AXIS_TURNTABLE] = s32(thumb_left_y()) * input_device::RELATIVE_PER_PIXEL * 2; + m_axes[AXIS_CROSSFADE] = normalize_absolute_axis(thumb_right_y(), XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + } - // Copy the XState into State - // Start with the POV (DPAD) - for (int povindex = 0; povindex < XINPUT_MAX_POV; povindex++) + // handle effect dial + if (was_reset) + { + // just grab the current count after regaining focus + m_prev_effect = u16(thumb_right_x()); + } + else if (relative_reset) { - int currentPov = xinput_pov_dir[povindex]; - gamepad.povs[povindex] = (xinput_state.xstate.Gamepad.wButtons & currentPov) ? 0xFF : 0; + // convert value to relative displacement + s32 effect_delta = s32(u32(u16(thumb_right_x()))) - s32(u32(m_prev_effect)); + m_prev_effect = u16(thumb_right_x()); + if (0x8000 < effect_delta) + effect_delta -= 0x1'0000; + else if (-0x8000 > effect_delta) + effect_delta += 0x1'0000; + m_axes[AXIS_EFFECT] = effect_delta * input_device::RELATIVE_PER_PIXEL / 128; } +} - // Now do the buttons - for (int buttonindex = 0; buttonindex < XINPUT_MAX_BUTTONS; buttonindex++) + +void xinput_turntable_device::reset() +{ + set_reset(); + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_turntable_device::configure(input_device &device) +{ + // track item IDs for setting up default assignments + input_device::assignment_vector assignments; + input_item_id switch_ids[SWITCH_TOTAL]; + std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID); + + // add axes + input_item_id const turntable_id = device.add_item( + "Turntable", + std::string_view(), + ITEM_ID_ADD_RELATIVE1, + generic_axis_get_state<s32>, + &m_axes[AXIS_TURNTABLE]); + input_item_id const effect_id = device.add_item( + "Effect", + std::string_view(), + ITEM_ID_ADD_RELATIVE2, + generic_axis_get_state<s32>, + &m_axes[AXIS_EFFECT]); + input_item_id const crossfade_id = device.add_item( + "Crossfade", + std::string_view(), + ITEM_ID_XAXIS, + generic_axis_get_state<s32>, + &m_axes[AXIS_CROSSFADE]); + + // add hats + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) + { + if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i])) + { + switch_ids[SWITCH_DPAD_UP + i] = device.add_item( + HAT_NAMES_GAMEPAD[i], + std::string_view(), + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<u8>, + &m_switches[SWITCH_DPAD_UP + i]); + } + } + + // add buttons + input_item_id button_id = ITEM_ID_BUTTON1; + unsigned button_count = 0; + unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1]; + for (unsigned i = 0; (SWITCH_RSB - SWITCH_A) >= i; ++i) + { + if (has_button(SWITCH_BITS[i])) + { + switch_ids[SWITCH_A + i] = device.add_item( + BUTTON_NAMES_KEYBOARD[i], + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_A + i]); + numbered_buttons[button_count] = SWITCH_A + i; + + // use these for automatically numbered buttons and pedals + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_A + i])); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq); + if (3 > button_count) + assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq); + ++button_count; + } + } + + // turntable buttons activate these as well as A/B/X + switch_ids[SWITCH_GREEN] = device.add_item( + "Green", + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_GREEN]); + switch_ids[SWITCH_RED] = device.add_item( + "Red", + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_RED]); + switch_ids[SWITCH_BLUE] = device.add_item( + "Blue", + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_BLUE]); + + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + switch_ids[SWITCH_START] = device.add_item( + "Start", + std::string_view(), + ITEM_ID_START, + generic_button_get_state<u8>, + &m_switches[SWITCH_START]); + add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] }); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + switch_ids[SWITCH_BACK] = device.add_item( + "Back", + std::string_view(), + ITEM_ID_SELECT, + generic_button_get_state<u8>, + &m_switches[SWITCH_BACK]); + add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] }); + } + + // use D-pad and A/B/X/Y diamond for twin sticks + add_twin_stick_assignments( + assignments, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN], + switch_ids[SWITCH_X], + switch_ids[SWITCH_B], + switch_ids[SWITCH_Y], + switch_ids[SWITCH_A]); + + // for most analog player controls, use turntable for X and effect for Y + input_seq const turntable_seq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, turntable_id)); + input_seq const effect_seq(make_code(ITEM_CLASS_RELATIVE, ITEM_MODIFIER_NONE, effect_id)); + input_seq const crossfade_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NONE, crossfade_id)); + input_seq joystick_left_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, turntable_id)); + input_seq joystick_right_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, turntable_id)); + input_seq joystick_up_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, effect_id)); + input_seq joystick_down_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, effect_id)); + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_STANDARD, turntable_seq); + assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_STANDARD, effect_seq); + assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_STANDARD, crossfade_seq); + assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_STANDARD, effect_seq); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_STANDARD, turntable_seq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_STANDARD, effect_seq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_STANDARD, turntable_seq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_STANDARD, effect_seq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_STANDARD, turntable_seq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_STANDARD, effect_seq); + assignments.emplace_back(IPT_AD_STICK_Z, SEQ_TYPE_STANDARD, crossfade_seq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_STANDARD, turntable_seq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_STANDARD, effect_seq); + assignments.emplace_back(IPT_MOUSE_X, SEQ_TYPE_STANDARD, turntable_seq); + assignments.emplace_back(IPT_MOUSE_Y, SEQ_TYPE_STANDARD, effect_seq); + + // use D-pad for analog controls as well if present + bool const have_dpad_left = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_LEFT]; + bool const have_dpad_right = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_RIGHT]; + bool const have_dpad_up = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_UP]; + bool const have_dpad_down = ITEM_ID_INVALID != switch_ids[SWITCH_DPAD_DOWN]; + if (have_dpad_left) + { + input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_LEFT])); + input_seq const left_seq(code); + joystick_left_seq += input_seq::or_code; + joystick_left_seq += code; + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_DECREMENT, left_seq); + assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_DECREMENT, left_seq); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_DECREMENT, left_seq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_DECREMENT, left_seq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_DECREMENT, left_seq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_DECREMENT, left_seq); + } + if (have_dpad_right) + { + input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_RIGHT])); + input_seq const right_seq(code); + joystick_right_seq += input_seq::or_code; + joystick_right_seq += code; + assignments.emplace_back(IPT_PADDLE, SEQ_TYPE_INCREMENT, right_seq); + assignments.emplace_back(IPT_POSITIONAL, SEQ_TYPE_INCREMENT, right_seq); + assignments.emplace_back(IPT_DIAL, SEQ_TYPE_INCREMENT, right_seq); + assignments.emplace_back(IPT_TRACKBALL_X, SEQ_TYPE_INCREMENT, right_seq); + assignments.emplace_back(IPT_AD_STICK_X, SEQ_TYPE_INCREMENT, right_seq); + assignments.emplace_back(IPT_LIGHTGUN_X, SEQ_TYPE_INCREMENT, right_seq); + } + if (have_dpad_up) + { + input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_UP])); + input_seq const up_seq(code); + joystick_up_seq += input_seq::or_code; + joystick_up_seq += code; + assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_DECREMENT, up_seq); + assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_DECREMENT, up_seq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_DECREMENT, up_seq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_DECREMENT, up_seq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_DECREMENT, up_seq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_DECREMENT, up_seq); + } + if (have_dpad_down) + { + input_code const code(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_DOWN])); + input_seq const down_seq(code); + joystick_down_seq += input_seq::or_code; + joystick_down_seq += code; + assignments.emplace_back(IPT_PADDLE_V, SEQ_TYPE_INCREMENT, down_seq); + assignments.emplace_back(IPT_POSITIONAL_V, SEQ_TYPE_INCREMENT, down_seq); + assignments.emplace_back(IPT_DIAL_V, SEQ_TYPE_INCREMENT, down_seq); + assignments.emplace_back(IPT_TRACKBALL_Y, SEQ_TYPE_INCREMENT, down_seq); + assignments.emplace_back(IPT_AD_STICK_Y, SEQ_TYPE_INCREMENT, down_seq); + assignments.emplace_back(IPT_LIGHTGUN_Y, SEQ_TYPE_INCREMENT, down_seq); + } + assignments.emplace_back(IPT_JOYSTICK_LEFT, SEQ_TYPE_STANDARD, joystick_left_seq); + assignments.emplace_back(IPT_JOYSTICK_RIGHT, SEQ_TYPE_STANDARD, joystick_right_seq); + assignments.emplace_back(IPT_JOYSTICK_UP, SEQ_TYPE_STANDARD, joystick_up_seq); + assignments.emplace_back(IPT_JOYSTICK_DOWN, SEQ_TYPE_STANDARD, joystick_down_seq); + + // choose navigation controls + input_seq ui_up_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, turntable_id)); + input_seq ui_down_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, turntable_id)); + input_seq ui_left_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, effect_id)); + input_seq ui_right_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, effect_id)); + if ((have_dpad_up && have_dpad_down) || !have_dpad_left || !have_dpad_right) + { + if (have_dpad_up) + { + ui_up_seq += input_seq::or_code; + ui_up_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_UP]); + } + if (have_dpad_down) + { + ui_down_seq += input_seq::or_code; + ui_down_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_DOWN]); + } + if (have_dpad_left) + { + ui_left_seq += input_seq::or_code; + ui_left_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_LEFT]); + } + if (have_dpad_right) + { + ui_right_seq += input_seq::or_code; + ui_right_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_RIGHT]); + } + } + else { - int currentButton = xinput_buttons[buttonindex]; - gamepad.buttons[buttonindex] = (xinput_state.xstate.Gamepad.wButtons & currentButton) ? 0xFF : 0; + if (have_dpad_left) + { + ui_up_seq += input_seq::or_code; + ui_up_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_LEFT]); + } + if (have_dpad_right) + { + ui_down_seq += input_seq::or_code; + ui_down_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_RIGHT]); + } + if (have_dpad_up) + { + ui_left_seq += input_seq::or_code; + ui_left_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_UP]); + } + if (have_dpad_down) + { + ui_right_seq += input_seq::or_code; + ui_right_seq += make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_DPAD_DOWN]); + } } + assignments.emplace_back(IPT_UI_UP, SEQ_TYPE_STANDARD, ui_up_seq); + assignments.emplace_back(IPT_UI_DOWN, SEQ_TYPE_STANDARD, ui_down_seq); + assignments.emplace_back(IPT_UI_LEFT, SEQ_TYPE_STANDARD, ui_left_seq); + assignments.emplace_back(IPT_UI_RIGHT, SEQ_TYPE_STANDARD, ui_right_seq); - // Now grab the axis values - // Each of the thumbstick axis members is a signed value between -32768 and 32767 describing the position of the thumbstick - // However, the Y axis values are inverted from what MAME expects, so negate the value - gamepad.left_thumb_x = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbLX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - gamepad.left_thumb_y = normalize_absolute_axis(-xinput_state.xstate.Gamepad.sThumbLY, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - gamepad.right_thumb_x = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbRX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - gamepad.right_thumb_y = normalize_absolute_axis(-xinput_state.xstate.Gamepad.sThumbRY, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + // assign UI select/back/clear/help + assign_ui_actions( + assignments, + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_START, + SWITCH_BACK, + switch_ids, + numbered_buttons, + std::min<unsigned>(button_count, 4)); - // Now the triggers, place them on half-axes (negative side) - gamepad.left_trigger = -normalize_absolute_axis(xinput_state.xstate.Gamepad.bLeftTrigger, -255, 255); - gamepad.right_trigger = -normalize_absolute_axis(xinput_state.xstate.Gamepad.bRightTrigger, -255, 255); + // set default assignments + device.set_default_assignments(std::move(assignments)); } -void xinput_joystick_device::reset() + + +//============================================================ +// XInput keyboard handler +//============================================================ + +class xinput_keyboard_device : public xinput_device_base { - memset(&gamepad, 0, sizeof(gamepad)); +public: + xinput_keyboard_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper); + + virtual void poll(bool relative_reset) override; + virtual void reset() override; + virtual void configure(input_device &device) override; + +private: + static inline constexpr USHORT SWITCH_BITS[] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT + }; + + enum + { + SWITCH_A, // button bits + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_LB, + SWITCH_RB, + SWITCH_LSB, + SWITCH_RSB, + SWITCH_START, + SWITCH_BACK, + + SWITCH_DPAD_UP, // D-pad bits + SWITCH_DPAD_DOWN, + SWITCH_DPAD_LEFT, + SWITCH_DPAD_RIGHT, + + SWITCH_C1, + SWITCH_C3 = SWITCH_C1 + 24, + + SWITCH_TOTAL + }; + + enum + { + AXIS_VELOCITY, // field in LSX + AXIS_PEDAL, // RSY, most positive value neutral + + AXIS_TOTAL + }; + + u8 m_switches[SWITCH_TOTAL]; + s32 m_axes[AXIS_TOTAL]; +}; + + +xinput_keyboard_device::xinput_keyboard_device( + std::string &&name, + std::string &&id, + input_module &module, + u32 player, + XINPUT_CAPABILITIES const &caps, + xinput_api_helper const &helper) : + xinput_device_base(std::move(name), std::move(id), module, player, caps, helper) +{ + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); } -void xinput_joystick_device::configure() + +void xinput_keyboard_device::poll(bool relative_reset) { - std::lock_guard<std::mutex> scope_lock(m_device_lock); + // TODO: how many bits are really velocity? + // TODO: how are touch strip and overdrive read? - if (m_configured) + // poll the device first, and skip if nothing changed + if (!read_state()) return; - // Add the axes - for (int axisnum = 0; axisnum < XINPUT_MAX_AXIS; axisnum++) + // translate button bits + for (unsigned i = 0; std::size(SWITCH_BITS) > i; ++i) + m_switches[SWITCH_A + i] = (buttons() & SWITCH_BITS[i]) ? 0xff : 0x00; + + // translate keys + for (unsigned i = 0; 8 > i; ++i) + { + m_switches[SWITCH_C1 + i] = BIT(trigger_left(), 7 - i) ? 0xff : 0x00; + m_switches[SWITCH_C1 + 8 + i] = BIT(trigger_right(), 7 - i) ? 0xff : 0x00; + m_switches[SWITCH_C1 + 16 + i] = BIT(thumb_left_x(), 7 - i) ? 0xff : 0x00; + } + m_switches[SWITCH_C3] = BIT(thumb_left_x(), 15) ? 0xff : 0x00; + + // translate axes + m_axes[AXIS_VELOCITY] = -normalize_absolute_axis(BIT(thumb_left_x(), 8, 7), -127, 127); + m_axes[AXIS_PEDAL] = -normalize_absolute_axis(32'767 - thumb_right_y(), -32'767, 32'767); +} + + +void xinput_keyboard_device::reset() +{ + set_reset(); + std::fill(std::begin(m_switches), std::end(m_switches), 0); + std::fill(std::begin(m_axes), std::end(m_axes), 0); +} + + +void xinput_keyboard_device::configure(input_device &device) +{ + // track item IDs for setting up default assignments + input_device::assignment_vector assignments; + input_item_id switch_ids[SWITCH_TOTAL]; + std::fill(std::begin(switch_ids), std::end(switch_ids), ITEM_ID_INVALID); + + // add axes + device.add_item( + "Velocity", + std::string_view(), + ITEM_ID_SLIDER1, + generic_axis_get_state<s32>, + &m_axes[AXIS_VELOCITY]); + input_item_id const pedal_id = device.add_item( + "Pedal", + std::string_view(), + ITEM_ID_SLIDER2, + generic_axis_get_state<s32>, + &m_axes[AXIS_PEDAL]); + assignments.emplace_back( + IPT_PEDAL, + SEQ_TYPE_STANDARD, + input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedal_id))); + + // add hats + for (unsigned i = 0; (SWITCH_DPAD_RIGHT - SWITCH_DPAD_UP) >= i; ++i) { - device()->add_item( - xinput_axis_name[axisnum], - xinput_axis_ids[axisnum], - generic_axis_get_state<LONG>, - &gamepad.left_thumb_x + axisnum); + if (has_button(SWITCH_BITS[SWITCH_DPAD_UP + i])) + { + switch_ids[SWITCH_DPAD_UP + i] = device.add_item( + HAT_NAMES_GAMEPAD[i], + std::string_view(), + input_item_id(ITEM_ID_HAT1UP + i), // matches up/down/left/right order + generic_button_get_state<u8>, + &m_switches[SWITCH_DPAD_UP + i]); + } } - // Populate the POVs - // For XBOX, we treat the DPAD as a hat switch - for (int povnum = 0; povnum < XINPUT_MAX_POV; povnum++) + // add buttons + input_item_id button_id = ITEM_ID_BUTTON1; + unsigned button_count = 0; + unsigned numbered_buttons[SWITCH_RSB - SWITCH_A + 1]; + for (unsigned i = 0; (SWITCH_RSB - SWITCH_A) >= i; ++i) { - device()->add_item( - xinput_pov_names[povnum], - ITEM_ID_OTHER_SWITCH, - generic_button_get_state<BYTE>, - &gamepad.povs[povnum]); + if (has_button(SWITCH_BITS[i])) + { + switch_ids[SWITCH_A + i] = device.add_item( + BUTTON_NAMES_KEYBOARD[i], + std::string_view(), + button_id++, + generic_button_get_state<u8>, + &m_switches[SWITCH_A + i]); + numbered_buttons[button_count] = SWITCH_A + i; + + // use these for automatically numbered buttons and pedals + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_A + i])); + assignments.emplace_back(ioport_type(IPT_BUTTON1 + button_count), SEQ_TYPE_STANDARD, seq); + if (3 > button_count) + assignments.emplace_back(ioport_type(IPT_PEDAL + button_count), SEQ_TYPE_INCREMENT, seq); + ++button_count; + } } - // populate the buttons - for (int butnum = 0; butnum < XINPUT_MAX_BUTTONS; butnum++) + // add keys + char const *const key_formats[]{ + "C %d", "C# %d", "D %d", "D# %d", "E %d", "F %d", "F# %d", "G %d", "G# %d", "A %d", "A# %d", "B %d" }; + std::pair<ioport_type, ioport_type> const key_ids[]{ + { IPT_MAHJONG_A, IPT_HANAFUDA_A }, // C + { IPT_MAHJONG_SCORE, IPT_INVALID }, // C# + { IPT_MAHJONG_B, IPT_HANAFUDA_B }, // D + { IPT_MAHJONG_DOUBLE_UP, IPT_INVALID }, // D# + { IPT_MAHJONG_C, IPT_HANAFUDA_C }, // E + { IPT_MAHJONG_D, IPT_HANAFUDA_D }, // F + { IPT_MAHJONG_BIG, IPT_INVALID }, // F# + { IPT_MAHJONG_E, IPT_HANAFUDA_E }, // G + { IPT_MAHJONG_SMALL, IPT_INVALID }, // G# + { IPT_MAHJONG_F, IPT_HANAFUDA_F }, // A + { IPT_MAHJONG_LAST_CHANCE, IPT_INVALID }, // A# + { IPT_MAHJONG_G, IPT_HANAFUDA_G }, // B + { IPT_MAHJONG_H, IPT_HANAFUDA_H }, // C + { IPT_MAHJONG_KAN, IPT_INVALID }, // C# + { IPT_MAHJONG_I, IPT_INVALID }, // D + { IPT_MAHJONG_PON, IPT_INVALID }, // D# + { IPT_MAHJONG_J, IPT_INVALID }, // E + { IPT_MAHJONG_K, IPT_INVALID }, // F + { IPT_MAHJONG_CHI, IPT_INVALID }, // F# + { IPT_MAHJONG_L, IPT_INVALID }, // G + { IPT_MAHJONG_REACH, IPT_INVALID }, // G# + { IPT_MAHJONG_M, IPT_HANAFUDA_YES }, // A + { IPT_MAHJONG_RON, IPT_INVALID }, // A# + { IPT_MAHJONG_N, IPT_HANAFUDA_NO }, // B + { IPT_MAHJONG_O, IPT_INVALID } }; // C + for (unsigned i = 0; (SWITCH_C3 - SWITCH_C1) >= i; ++i) { - device()->add_item( - xinput_button_names[butnum], - static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state<BYTE>, - &gamepad.buttons[butnum]); + switch_ids[SWITCH_C1 + i] = device.add_item( + util::string_format(key_formats[i % 12], (i / 12) + 1), + std::string_view(), + (ITEM_ID_BUTTON32 >= button_id) ? button_id++ : ITEM_ID_OTHER_SWITCH, + generic_button_get_state<u8>, + &m_switches[SWITCH_C1 + i]); + + // add mahjong/hanafuda control assignments + input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, switch_ids[SWITCH_C1 + i])); + if (IPT_INVALID != key_ids[i].first) + assignments.emplace_back(key_ids[i].first, SEQ_TYPE_STANDARD, seq); + if (IPT_INVALID != key_ids[i].second) + assignments.emplace_back(key_ids[i].second, SEQ_TYPE_STANDARD, seq); } - device()->add_item( - "RT", - ITEM_ID_ZAXIS, - generic_axis_get_state<LONG>, - &gamepad.right_trigger); + // add start/back + if (has_button(XINPUT_GAMEPAD_START)) + { + switch_ids[SWITCH_START] = device.add_item( + "Start", + std::string_view(), + ITEM_ID_START, + generic_button_get_state<u8>, + &m_switches[SWITCH_START]); + add_button_assignment(assignments, IPT_START, { switch_ids[SWITCH_START] }); + } + if (has_button(XINPUT_GAMEPAD_BACK)) + { + switch_ids[SWITCH_BACK] = device.add_item( + "Back", + std::string_view(), + ITEM_ID_SELECT, + generic_button_get_state<u8>, + &m_switches[SWITCH_BACK]); + add_button_assignment(assignments, IPT_SELECT, { switch_ids[SWITCH_BACK] }); + } - device()->add_item( - "LT", - ITEM_ID_RZAXIS, - generic_axis_get_state<LONG>, - &gamepad.left_trigger); + // use the D-pad for directional controls + add_directional_assignments( + assignments, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN]); + + // use the D-pad and A/B/X/Y diamond for twin sticks + add_twin_stick_assignments( + assignments, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + ITEM_ID_INVALID, + switch_ids[SWITCH_DPAD_LEFT], + switch_ids[SWITCH_DPAD_RIGHT], + switch_ids[SWITCH_DPAD_UP], + switch_ids[SWITCH_DPAD_DOWN], + switch_ids[SWITCH_X], + switch_ids[SWITCH_B], + switch_ids[SWITCH_Y], + switch_ids[SWITCH_A]); + + // assign UI select/back/clear/help + assign_ui_actions( + assignments, + SWITCH_B, + SWITCH_X, + SWITCH_Y, + SWITCH_START, + SWITCH_BACK, + switch_ids, + numbered_buttons, + button_count); + + // set default assignments + device.set_default_assignments(std::move(assignments)); +} + + + +//============================================================ +// XInput joystick module +//============================================================ + +class xinput_joystick_module : public input_module_impl<device_info, osd_common_t> +{ +public: + xinput_joystick_module() : input_module_impl<device_info, osd_common_t>(OSD_JOYSTICKINPUT_PROVIDER, "xinput") + { + } + + virtual int init(osd_interface &osd, const osd_options &options) override + { + int status; + + // Call the base + status = input_module_impl<device_info, osd_common_t>::init(osd, options); + if (status != 0) + return status; + + // Create and initialize our helper + m_xinput_helper = std::make_unique<xinput_api_helper>(); + status = m_xinput_helper->initialize(); + if (status != 0) + { + osd_printf_error("xinput_joystick_module failed to get XInput interface! Error: %u\n", static_cast<unsigned int>(status)); + return -1; + } + + return 0; + } + + virtual void input_init(running_machine &machine) override + { + input_module_impl<device_info, osd_common_t>::input_init(machine); + + // Loop through each gamepad to determine if they are connected + for (UINT i = 0; i < XUSER_MAX_COUNT; i++) + { + // allocate and link in a new device + auto devinfo = m_xinput_helper->create_xinput_device(i, *this); + if (devinfo) + add_device(DEVICE_CLASS_JOYSTICK, std::move(devinfo)); + } + } + + virtual void exit() override + { + input_module_impl<device_info, osd_common_t>::exit(); - m_configured = true; + m_xinput_helper.reset(); + } + +private: + std::unique_ptr<xinput_api_helper> m_xinput_helper; +}; + +} // anonymous namespace + + + +int xinput_api_helper::initialize() +{ + m_xinput_dll = dynamic_module::open(XINPUT_LIBRARIES); + + XInputGetState = m_xinput_dll->bind<xinput_get_state_fn>("XInputGetState"); + XInputGetCapabilities = m_xinput_dll->bind<xinput_get_caps_fn>("XInputGetCapabilities"); + + if (!XInputGetState || !XInputGetCapabilities) + { + osd_printf_error("XInput: Could not find API functions.\n"); + return -1; + } + + return 0; +} + + +//============================================================ +// create_xinput_device +//============================================================ + +std::unique_ptr<device_info> xinput_api_helper::create_xinput_device( + UINT index, + input_module_base &module) +{ + // If we can't get the capabilities skip this device + XINPUT_STATE state{ 0 }; + if (xinput_get_state(index, &state) != ERROR_SUCCESS) + return nullptr; + XINPUT_CAPABILITIES caps{ 0 }; + if (FAILED(xinput_get_capabilities(index, 0, &caps))) + return nullptr; + + char device_name[16]; + snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1); + + // allocate specialised device objects + switch (caps.Type) + { + case XINPUT_DEVTYPE_GAMEPAD: + switch (caps.SubType) + { + case 0x04: // XINPUT_DEVSUBTYPE_FLIGHT_STICK: work around MinGW header issues + return std::make_unique<xinput_flight_stick_device>( + device_name, + device_name, + module, + index, + caps, + *this); + case XINPUT_DEVSUBTYPE_GUITAR: + case XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE: + case XINPUT_DEVSUBTYPE_GUITAR_BASS: + return std::make_unique<xinput_guitar_device>( + device_name, + device_name, + module, + index, + caps, + *this); + case XINPUT_DEVSUBTYPE_DRUM_KIT: + return std::make_unique<xinput_drumkit_device>( + device_name, + device_name, + module, + index, + caps, + *this); + case 0x0f: + return std::make_unique<xinput_keyboard_device>( + device_name, + device_name, + module, + index, + caps, + *this); + case 0x17: + return std::make_unique<xinput_turntable_device>( + device_name, + device_name, + module, + index, + caps, + *this); + } + } + + // create default general-purpose device + return std::make_unique<xinput_joystick_device>( + device_name, + device_name, + module, + index, + caps, + *this); } -#else // defined(OSD_WINDOWS) +} // namespace osd + +#else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) #include "input_module.h" -MODULE_NOT_SUPPORTED(xinput_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "xinput") +namespace osd { namespace { MODULE_NOT_SUPPORTED(xinput_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "xinput") } } -#endif // defined(OSD_WINDOWS) +#endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -MODULE_DEFINITION(JOYSTICKINPUT_XINPUT, xinput_joystick_module) +MODULE_DEFINITION(JOYSTICKINPUT_XINPUT, osd::xinput_joystick_module) diff --git a/src/osd/modules/input/input_xinput.h b/src/osd/modules/input/input_xinput.h index bd918f9aacb..fa36b7a2622 100644 --- a/src/osd/modules/input/input_xinput.h +++ b/src/osd/modules/input/input_xinput.h @@ -1,30 +1,30 @@ +// license:BSD-3-Clause +// copyright-holders:Brad Hughes, Vas Crabb #ifndef MAME_OSD_INPUT_INPUT_XINPUT_H #define MAME_OSD_INPUT_INPUT_XINPUT_H #pragma once -#include "input_windows.h" +#include "input_common.h" #include "modules/lib/osdlib.h" -#include <cstdint> #include <memory> -#include <mutex> -#include <string_view> +#include <windows.h> #include <xinput.h> -class xinput_joystick_device; +namespace osd { - -class xinput_api_helper : public std::enable_shared_from_this<xinput_api_helper> +class xinput_api_helper { public: xinput_api_helper() { } int initialize(); - xinput_joystick_device *create_xinput_device(running_machine &machine, UINT index, wininput_module &module); + + std::unique_ptr<device_info> create_xinput_device(UINT index, input_module_base &module); DWORD xinput_get_state(DWORD dwUserindex, XINPUT_STATE *pState) const { @@ -41,53 +41,11 @@ private: typedef DWORD (WINAPI *xinput_get_state_fn)(DWORD, XINPUT_STATE *); typedef DWORD (WINAPI *xinput_get_caps_fn)(DWORD, DWORD, XINPUT_CAPABILITIES *); - osd::dynamic_module::ptr m_xinput_dll = nullptr; - xinput_get_state_fn XInputGetState = nullptr; - xinput_get_caps_fn XInputGetCapabilities = nullptr; + dynamic_module::ptr m_xinput_dll = nullptr; + xinput_get_state_fn XInputGetState = nullptr; + xinput_get_caps_fn XInputGetCapabilities = nullptr; }; - -class xinput_joystick_device : public device_info -{ -public: - static inline constexpr int XINPUT_MAX_POV = 4; - static inline constexpr int XINPUT_MAX_BUTTONS = 10; - static inline constexpr int XINPUT_MAX_AXIS = 4; - - struct gamepad_state - { - BYTE buttons[XINPUT_MAX_BUTTONS]; - BYTE povs[XINPUT_MAX_POV]; - LONG left_trigger; - LONG right_trigger; - LONG left_thumb_x; - LONG left_thumb_y; - LONG right_thumb_x; - LONG right_thumb_y; - }; - - // state information for a gamepad; state must be first element - struct xinput_api_state - { - uint32_t player_index; - XINPUT_STATE xstate; - XINPUT_CAPABILITIES caps; - }; - - gamepad_state gamepad; - xinput_api_state xinput_state; - -private: - std::shared_ptr<xinput_api_helper> m_xinput_helper; - std::mutex m_device_lock; - bool m_configured; - -public: - xinput_joystick_device(running_machine &machine, std::string &&name, std::string &&id, input_module &module, std::shared_ptr<xinput_api_helper> helper); - - void poll() override; - void reset() override; - void configure(); -}; +} // namespace osd #endif // MAME_OSD_INPUT_INPUT_XINPUT_H diff --git a/src/osd/modules/lib/osdlib.h b/src/osd/modules/lib/osdlib.h index 8ca4762a111..967cb0d61ae 100644 --- a/src/osd/modules/lib/osdlib.h +++ b/src/osd/modules/lib/osdlib.h @@ -12,16 +12,21 @@ // - osd_ticks // - osd_sleep //============================================================ +#ifndef MAME_OSD_LIB_OSDLIB_H +#define MAME_OSD_LIB_OSDLIB_H -#ifndef __OSDLIB__ -#define __OSDLIB__ +#pragma once +#include <cstdint> #include <initializer_list> #include <string> +#include <string_view> +#include <system_error> #include <type_traits> #include <vector> #include <memory> + /*----------------------------------------------------------------------------- osd_process_kill: kill the current process @@ -54,15 +59,27 @@ void osd_process_kill(); int osd_setenv(const char *name, const char *value, int overwrite); -/*----------------------------------------------------------------------------- - osd_get_clipboard_text: retrieves text from the clipboard ------------------------------------------------------------------------------*/ -std::string osd_get_clipboard_text(); +/// \brief Get clipboard text +/// +/// Gets current clipboard content as UTF-8 text. Returns an empty +/// string if the clipboard contents cannot be converted to plain text. +/// \return Clipboard contents or an empty string. +std::string osd_get_clipboard_text() noexcept; + + +/// \brief Set clipboard text +/// +/// Sets the desktop environment's clipboard contents to the supplied +/// UTF-8 text. The contents of the clipboard may be changed on error. +/// \param [in] text The text to copy to the clipboard. +/// \return An error condition if the operation failed or is +/// unsupported. +std::error_condition osd_set_clipboard_text(std::string_view text) noexcept; namespace osd { -bool invalidate_instruction_cache(void const *start, std::size_t size); +bool invalidate_instruction_cache(void const *start, std::size_t size) noexcept; class virtual_memory_allocation @@ -82,12 +99,12 @@ public: virtual_memory_allocation(virtual_memory_allocation const &) = delete; virtual_memory_allocation &operator=(virtual_memory_allocation const &) = delete; - virtual_memory_allocation() { } - virtual_memory_allocation(std::initializer_list<std::size_t> blocks, unsigned intent) + virtual_memory_allocation() noexcept { } + virtual_memory_allocation(std::initializer_list<std::size_t> blocks, unsigned intent) noexcept { m_memory = do_alloc(blocks, intent, m_size, m_page_size); } - virtual_memory_allocation(virtual_memory_allocation &&that) : m_memory(that.m_memory), m_size(that.m_size), m_page_size(that.m_page_size) + virtual_memory_allocation(virtual_memory_allocation &&that) noexcept : m_memory(that.m_memory), m_size(that.m_size), m_page_size(that.m_page_size) { that.m_memory = nullptr; that.m_size = that.m_page_size = 0U; @@ -98,12 +115,12 @@ public: do_free(m_memory, m_size); } - explicit operator bool() const { return bool(m_memory); } - void *get() { return m_memory; } - std::size_t size() const { return m_size; } - std::size_t page_size() const { return m_page_size; } + explicit operator bool() const noexcept { return bool(m_memory); } + void *get() noexcept { return m_memory; } + std::size_t size() const noexcept { return m_size; } + std::size_t page_size() const noexcept { return m_page_size; } - bool set_access(std::size_t start, std::size_t size, unsigned access) + bool set_access(std::size_t start, std::size_t size, unsigned access) noexcept { if ((start % m_page_size) || (size % m_page_size) || (start > m_size) || ((m_size - start) < size)) return false; @@ -111,7 +128,7 @@ public: return do_set_access(reinterpret_cast<std::uint8_t *>(m_memory) + start, size, access); } - virtual_memory_allocation &operator=(std::nullptr_t) + virtual_memory_allocation &operator=(std::nullptr_t) noexcept { if (m_memory) do_free(m_memory, m_size); @@ -120,7 +137,7 @@ public: return *this; } - virtual_memory_allocation &operator=(virtual_memory_allocation &&that) + virtual_memory_allocation &operator=(virtual_memory_allocation &&that) noexcept { if (&that != this) { @@ -136,9 +153,9 @@ public: } private: - static void *do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size); - static void do_free(void *start, std::size_t size); - static bool do_set_access(void *start, std::size_t size, unsigned access); + static void *do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) noexcept; + static void do_free(void *start, std::size_t size) noexcept; + static bool do_set_access(void *start, std::size_t size, unsigned access) noexcept; void *m_memory = nullptr; std::size_t m_size = 0U, m_page_size = 0U; @@ -193,4 +210,4 @@ protected: #define OSD_DYNAMIC_CALL(fname, ...) (*m_##fname##_pfn) ( __VA_ARGS__ ) #define OSD_DYNAMIC_API_TEST(fname) (m_##fname##_pfn != nullptr) -#endif /* __OSDLIB__ */ +#endif // MAME_OSD_LIB_OSDLIB_H diff --git a/src/osd/modules/lib/osdlib_macosx.cpp b/src/osd/modules/lib/osdlib_macosx.cpp index 19b228b6369..9d13f118ff2 100644 --- a/src/osd/modules/lib/osdlib_macosx.cpp +++ b/src/osd/modules/lib/osdlib_macosx.cpp @@ -85,7 +85,7 @@ void osd_break_into_debugger(const char *message) // osd_get_clipboard_text //============================================================ -std::string osd_get_clipboard_text() +std::string osd_get_clipboard_text() noexcept { std::string result; bool has_result = false; @@ -121,31 +121,48 @@ std::string osd_get_clipboard_text() CFStringEncoding encoding; if (UTTypeConformsTo(flavor_type, kUTTypeUTF16PlainText)) encoding = kCFStringEncodingUTF16; - else if (UTTypeConformsTo (flavor_type, kUTTypeUTF8PlainText)) + else if (UTTypeConformsTo(flavor_type, kUTTypeUTF8PlainText)) encoding = kCFStringEncodingUTF8; - else if (UTTypeConformsTo (flavor_type, kUTTypePlainText)) + else if (UTTypeConformsTo(flavor_type, kUTTypePlainText)) encoding = kCFStringEncodingMacRoman; else continue; CFDataRef flavor_data; err = PasteboardCopyItemFlavorData(pasteboard_ref, item_id, flavor_type, &flavor_data); + if (err) + continue; - if (!err) + CFDataRef utf8_data; + if (kCFStringEncodingUTF8 == encoding) + { + utf8_data = flavor_data; + } + else { CFStringRef string_ref = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, flavor_data, encoding); - CFDataRef data_ref = CFStringCreateExternalRepresentation (kCFAllocatorDefault, string_ref, kCFStringEncodingUTF8, '?'); - CFRelease(string_ref); CFRelease(flavor_data); + if (!string_ref) + continue; - CFIndex const length = CFDataGetLength(data_ref); - CFRange const range = CFRangeMake(0, length); - - result.resize(length); - CFDataGetBytes(data_ref, range, reinterpret_cast<unsigned char *>(&result[0])); - has_result = true; + utf8_data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, string_ref, kCFStringEncodingUTF8, '?'); + CFRelease(string_ref); + } - CFRelease(data_ref); + if (utf8_data) + { + CFIndex const length = CFDataGetLength(utf8_data); + CFRange const range = CFRangeMake(0, length); + try + { + result.resize(length); + CFDataGetBytes(utf8_data, range, reinterpret_cast<UInt8 *>(result.data())); + has_result = true; + } + catch (std::bad_alloc const &) + { + } + CFRelease(utf8_data); } } @@ -157,11 +174,51 @@ std::string osd_get_clipboard_text() return result; } + +//============================================================ +// osd_set_clipboard_text +//============================================================ + +std::error_condition osd_set_clipboard_text(std::string_view text) noexcept +{ + // FIXME: better conversion of OSStatus to std::error_condition + OSStatus err; + + CFDataRef const data = CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 const *>(text.data()), text.length()); + if (!data) + return std::errc::not_enough_memory; + + PasteboardRef pasteboard_ref; + err = PasteboardCreate(kPasteboardClipboard, &pasteboard_ref); + if (err) + { + CFRelease(data); + return std::errc::io_error; + } + + err = PasteboardClear(pasteboard_ref); + if (err) + { + CFRelease(data); + CFRelease(pasteboard_ref); + return std::errc::io_error; + } + + err = PasteboardPutItemFlavor(pasteboard_ref, PasteboardItemID(1), kUTTypeUTF8PlainText, data, kPasteboardFlavorNoFlags); + CFRelease(data); + CFRelease(pasteboard_ref); + if (err) + return std::errc::io_error; + + return std::error_condition(); +} + + //============================================================ // osd_getpid //============================================================ -int osd_getpid() +int osd_getpid() noexcept { return getpid(); } @@ -225,7 +282,7 @@ private: } // anonymous namespace -bool invalidate_instruction_cache(void const *start, std::size_t size) +bool invalidate_instruction_cache(void const *start, std::size_t size) noexcept { char const *const begin(reinterpret_cast<char const *>(start)); char const *const end(begin + size); @@ -234,7 +291,7 @@ bool invalidate_instruction_cache(void const *start, std::size_t size) } -void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) noexcept { long const p(sysconf(_SC_PAGE_SIZE)); if (0 >= p) @@ -253,12 +310,12 @@ void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blo return result; } -void virtual_memory_allocation::do_free(void *start, std::size_t size) +void virtual_memory_allocation::do_free(void *start, std::size_t size) noexcept { munmap(start, size); } -bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) noexcept { int prot((NONE == access) ? PROT_NONE : 0); if (access & READ) diff --git a/src/osd/modules/lib/osdlib_unix.cpp b/src/osd/modules/lib/osdlib_unix.cpp index 4b091450116..20f6a9ab40c 100644 --- a/src/osd/modules/lib/osdlib_unix.cpp +++ b/src/osd/modules/lib/osdlib_unix.cpp @@ -69,35 +69,73 @@ void osd_break_into_debugger(const char *message) } #ifdef SDLMAME_ANDROID -std::string osd_get_clipboard_text() +std::string osd_get_clipboard_text() noexcept { return std::string(); } + +std::error_condition osd_set_clipboard_text(std::string_view text) noexcept +{ + return std::errc::io_error; // TODO: better error code? +} #else //============================================================ // osd_get_clipboard_text //============================================================ -std::string osd_get_clipboard_text() +std::string osd_get_clipboard_text() noexcept { + // TODO: better error handling std::string result; if (SDL_HasClipboardText()) { - char *temp = SDL_GetClipboardText(); - result.assign(temp); - SDL_free(temp); + char *const temp = SDL_GetClipboardText(); + if (temp) + { + try + { + result.assign(temp); + } + catch (std::bad_alloc const &) + { + } + SDL_free(temp); + } } return result; } + +//============================================================ +// osd_set_clipboard_text +//============================================================ + +std::error_condition osd_set_clipboard_text(std::string_view text) noexcept +{ + try + { + std::string const clip(text); // need to do this to ensure there's a terminating NUL for SDL + if (0 > SDL_SetClipboardText(clip.c_str())) + { + // SDL_GetError returns a message, can't really convert it to an error condition + return std::errc::io_error; // TODO: better error code? + } + + return std::error_condition(); + } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } +} #endif //============================================================ // osd_getpid //============================================================ -int osd_getpid() +int osd_getpid() noexcept { return getpid(); } @@ -161,7 +199,7 @@ private: } // anonymous namespace -bool invalidate_instruction_cache(void const *start, std::size_t size) +bool invalidate_instruction_cache(void const *start, std::size_t size) noexcept { #if !defined(SDLMAME_EMSCRIPTEN) char const *const begin(reinterpret_cast<char const *>(start)); @@ -172,7 +210,7 @@ bool invalidate_instruction_cache(void const *start, std::size_t size) } -void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) noexcept { long const p(sysconf(_SC_PAGE_SIZE)); if (0 >= p) @@ -209,12 +247,12 @@ void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blo return result; } -void virtual_memory_allocation::do_free(void *start, std::size_t size) +void virtual_memory_allocation::do_free(void *start, std::size_t size) noexcept { munmap(reinterpret_cast<char *>(start), size); } -bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) noexcept { int prot((NONE == access) ? PROT_NONE : 0); if (access & READ) diff --git a/src/osd/modules/lib/osdlib_win32.cpp b/src/osd/modules/lib/osdlib_win32.cpp index 174dc769760..d42ad14d3df 100644 --- a/src/osd/modules/lib/osdlib_win32.cpp +++ b/src/osd/modules/lib/osdlib_win32.cpp @@ -14,10 +14,10 @@ #include "osdcore.h" #include "strconv.h" -#ifdef OSD_WINDOWS #include "winutf8.h" -#endif +#include "winutil.h" +#include <algorithm> #include <cstdio> #include <cstdlib> @@ -168,25 +168,92 @@ static std::string convert_ansi(LPCVOID data) // osd_get_clipboard_text //============================================================ -std::string osd_get_clipboard_text() +std::string osd_get_clipboard_text() noexcept { std::string result; - // try to access unicode text - if (!get_clipboard_text_by_format(result, CF_UNICODETEXT, convert_wide)) + // TODO: better error handling + try + { + // try to access unicode text + if (!get_clipboard_text_by_format(result, CF_UNICODETEXT, convert_wide)) + { + // try to access ANSI text + get_clipboard_text_by_format(result, CF_TEXT, convert_ansi); + } + } + catch (...) { - // try to access ANSI text - get_clipboard_text_by_format(result, CF_TEXT, convert_ansi); } return result; } //============================================================ +// osd_set_clipboard_text +//============================================================ + +std::error_condition osd_set_clipboard_text(std::string_view text) noexcept +{ + try + { + // convert the text to a wide char string and create a moveable global block + std::wstring const wtext = osd::text::to_wstring(text); + HGLOBAL const clip = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(wchar_t)); + if (!clip) + return win_error_to_error_condition(GetLastError()); + LPWSTR const lock = reinterpret_cast<LPWSTR>(GlobalLock(clip)); + if (!lock) + { + DWORD const err(GetLastError()); + GlobalFree(clip); + return win_error_to_error_condition(err); + } + + // clear current clipboard contents + if (!OpenClipboard(nullptr)) + { + DWORD const err(GetLastError()); + GlobalUnlock(clip); + GlobalFree(clip); + return win_error_to_error_condition(err); + } + if (!EmptyClipboard()) + { + DWORD const err(GetLastError()); + CloseClipboard(); + GlobalUnlock(clip); + GlobalFree(clip); + return win_error_to_error_condition(err); + } + + // copy the text (plus NUL terminator) to the moveable block and put it on the clipboard + std::copy_n(wtext.c_str(), wtext.length() + 1, lock); + GlobalUnlock(clip); + if (!SetClipboardData(CF_UNICODETEXT, clip)) + { + DWORD const err(GetLastError()); + CloseClipboard(); + GlobalFree(clip); + return win_error_to_error_condition(err); + } + + // clean up + if (!CloseClipboard()) + return win_error_to_error_condition(GetLastError()); + return std::error_condition(); + } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } +} + +//============================================================ // osd_getpid //============================================================ -int osd_getpid() +int osd_getpid() noexcept { return GetCurrentProcessId(); } @@ -257,13 +324,13 @@ private: } // anonymous namespace -bool invalidate_instruction_cache(void const *start, std::size_t size) +bool invalidate_instruction_cache(void const *start, std::size_t size) noexcept { return FlushInstructionCache(GetCurrentProcess(), start, size) != 0; } -void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) +void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blocks, unsigned intent, std::size_t &size, std::size_t &page_size) noexcept { SYSTEM_INFO info; GetSystemInfo(&info); @@ -282,19 +349,19 @@ void *virtual_memory_allocation::do_alloc(std::initializer_list<std::size_t> blo return result; } -void virtual_memory_allocation::do_free(void *start, std::size_t size) +void virtual_memory_allocation::do_free(void *start, std::size_t size) noexcept { VirtualFree(start, 0, MEM_RELEASE); } -bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) +bool virtual_memory_allocation::do_set_access(void *start, std::size_t size, unsigned access) noexcept { - DWORD p, o; + DWORD p; if (access & EXECUTE) p = (access & WRITE) ? PAGE_EXECUTE_READWRITE : (access & READ) ? PAGE_EXECUTE_READ : PAGE_EXECUTE; else p = (access & WRITE) ? PAGE_READWRITE : (access & READ) ? PAGE_READONLY : PAGE_NOACCESS; - return VirtualProtect(start, size, p, &o) != 0; + return VirtualAlloc(start, size, MEM_COMMIT, p) != nullptr; } diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index 9adf9f2e604..430f6ceeac9 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -2,21 +2,25 @@ // copyright-holders:Aaron Giles /*************************************************************************** - osdepend.c + osdobj_common.h OS-dependent code interface. ***************************************************************************/ -#include "modules/lib/osdobj_common.h" +#include "osdobj_common.h" +#include "modules/osdwindow.h" +#include "modules/debugger/debug_module.h" #include "modules/font/font_module.h" #include "modules/input/input_module.h" -#include "modules/sound/sound_module.h" -#include "modules/debugger/debug_module.h" -#include "modules/netdev/netdev_module.h" #include "modules/midi/midi_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" @@ -27,151 +31,158 @@ const options_entry osd_options::s_option_entries[] = { - { nullptr, nullptr, core_options::option_type::HEADER, "OSD KEYBOARD MAPPING OPTIONS" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OSD INPUT MAPPING OPTIONS" }, #if defined(SDLMAME_MACOSX) || defined(OSD_MAC) - { OSDOPTION_UIMODEKEY, "DEL", core_options::option_type::STRING, "key to enable/disable MAME controls when emulated system has keyboard inputs" }, + { OSDOPTION_UIMODEKEY, "DEL", core_options::option_type::STRING, "key to enable/disable MAME controls when emulated system has keyboard inputs" }, #else - { OSDOPTION_UIMODEKEY, "SCRLOCK", core_options::option_type::STRING, "key to enable/disable MAME controls when emulated system has keyboard inputs" }, + { OSDOPTION_UIMODEKEY, "auto", core_options::option_type::STRING, "key to enable/disable MAME controls when emulated system has keyboard inputs" }, #endif // SDLMAME_MACOSX - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD FONT OPTIONS" }, - { OSD_FONT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for UI font: " }, - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD OUTPUT OPTIONS" }, - { OSD_OUTPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for output notifications: " }, - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD INPUT OPTIONS" }, - { OSD_KEYBOARDINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for keyboard input: " }, - { OSD_MOUSEINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for mouse input: " }, - { OSD_LIGHTGUNINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for lightgun input: " }, - { OSD_JOYSTICKINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for joystick input: " }, - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD CLI OPTIONS" }, - { OSDCOMMAND_LIST_MIDI_DEVICES ";mlist", "0", core_options::option_type::COMMAND, "list available MIDI I/O devices" }, - { OSDCOMMAND_LIST_NETWORK_ADAPTERS ";nlist", "0", core_options::option_type::COMMAND, "list available network adapters" }, - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD DEBUGGING OPTIONS" }, - { OSDOPTION_DEBUGGER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "debugger used: " }, - { 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" }, - { OSDOPTION_WATCHDOG ";wdog", "0", core_options::option_type::INTEGER, "force the program to terminate if no updates within specified number of seconds" }, - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD PERFORMANCE OPTIONS" }, - { OSDOPTION_NUMPROCESSORS ";np", OSDOPTVAL_AUTO, core_options::option_type::STRING, "number of processors; this overrides the number the system reports" }, - { OSDOPTION_BENCH, "0", core_options::option_type::INTEGER, "benchmark for the given number of emulated seconds; implies -video none -sound none -nothrottle" }, - - { nullptr, nullptr, core_options::option_type::HEADER, "OSD VIDEO OPTIONS" }, -// OS X can be trusted to have working hardware OpenGL, so default to it on for the best user experience - { OSDOPTION_VIDEO, OSDOPTVAL_AUTO, core_options::option_type::STRING, "video output method: " }, - { OSDOPTION_NUMSCREENS "(1-4)", "1", core_options::option_type::INTEGER, "number of output screens/windows to create; usually, you want just one" }, - { OSDOPTION_WINDOW ";w", "0", core_options::option_type::BOOLEAN, "enable window mode; otherwise, full screen mode is assumed" }, - { OSDOPTION_MAXIMIZE ";max", "1", core_options::option_type::BOOLEAN, "default to maximized windows" }, - { OSDOPTION_WAITVSYNC ";vs", "0", core_options::option_type::BOOLEAN, "enable waiting for the start of VBLANK before flipping screens (reduces tearing effects)" }, - { OSDOPTION_SYNCREFRESH ";srf", "0", core_options::option_type::BOOLEAN, "enable using the start of VBLANK for throttling instead of the game time" }, - { OSD_MONITOR_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "monitor discovery method: " }, + { OSDOPTION_CONTROLLER_MAP_FILE ";ctrlmap", OSDOPTVAL_NONE, core_options::option_type::PATH, "game controller mapping file" }, + { OSDOPTION_BACKGROUND_INPUT, "0", core_options::option_type::BOOLEAN, "don't ignore input when losing UI focus" }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD FONT OPTIONS" }, + { OSD_FONT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for UI font: " }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD OUTPUT OPTIONS" }, + { OSD_OUTPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for output notifications: " }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD INPUT OPTIONS" }, + { OSD_KEYBOARDINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for keyboard input: " }, + { OSD_MOUSEINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for mouse input: " }, + { OSD_LIGHTGUNINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for lightgun input: " }, + { OSD_JOYSTICKINPUT_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "provider for joystick input: " }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD CLI OPTIONS" }, + { OSDCOMMAND_LIST_MIDI_DEVICES ";mlist", "0", core_options::option_type::COMMAND, "list available MIDI I/O devices" }, + { OSDCOMMAND_LIST_NETWORK_ADAPTERS ";nlist", "0", core_options::option_type::COMMAND, "list available network adapters" }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD DEBUGGING OPTIONS" }, + { OSDOPTION_DEBUGGER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "debugger used: " }, + { 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" }, + { OSDOPTION_WATCHDOG ";wdog", "0", core_options::option_type::INTEGER, "force the program to terminate if no updates within specified number of seconds" }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD PERFORMANCE OPTIONS" }, + { OSDOPTION_NUMPROCESSORS ";np", OSDOPTVAL_AUTO, core_options::option_type::STRING, "number of processors; this overrides the number the system reports" }, + { OSDOPTION_BENCH, "0", core_options::option_type::INTEGER, "benchmark for the given number of emulated seconds; implies -video none -sound none -nothrottle" }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD VIDEO OPTIONS" }, + { OSDOPTION_VIDEO, OSDOPTVAL_AUTO, core_options::option_type::STRING, "video output method: " }, + { OSDOPTION_NUMSCREENS "(1-4)", "1", core_options::option_type::INTEGER, "number of output screens/windows to create; usually, you want just one" }, + { OSDOPTION_WINDOW ";w", "0", core_options::option_type::BOOLEAN, "enable window mode; otherwise, full screen mode is assumed" }, + { OSDOPTION_MAXIMIZE ";max", "1", core_options::option_type::BOOLEAN, "default to maximized windows" }, + { OSDOPTION_WAITVSYNC ";vs", "0", core_options::option_type::BOOLEAN, "enable waiting for the start of VBLANK before flipping screens (reduces tearing effects)" }, + { OSDOPTION_SYNCREFRESH ";srf", "0", core_options::option_type::BOOLEAN, "enable using the start of VBLANK for throttling instead of the game time" }, + { OSD_MONITOR_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "monitor discovery method: " }, // per-window options - { nullptr, nullptr, core_options::option_type::HEADER, "OSD PER-WINDOW VIDEO OPTIONS" }, - { OSDOPTION_SCREEN, OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the first screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_ASPECT ";screen_aspect", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio for all screens; 'auto' here will try to make a best guess" }, - { OSDOPTION_RESOLUTION ";r", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution for all screens; format is <width>x<height>[@<refreshrate>] or 'auto'" }, - { OSDOPTION_VIEW, OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for all screens" }, - - { OSDOPTION_SCREEN "0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the first screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_ASPECT "0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the first screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_RESOLUTION "0;r0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the first screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, - { OSDOPTION_VIEW "0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the first screen" }, - - { OSDOPTION_SCREEN "1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the second screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_ASPECT "1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the second screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_RESOLUTION "1;r1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the second screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, - { OSDOPTION_VIEW "1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the second screen" }, - - { OSDOPTION_SCREEN "2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the third screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_ASPECT "2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the third screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_RESOLUTION "2;r2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the third screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, - { OSDOPTION_VIEW "2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the third screen" }, - - { OSDOPTION_SCREEN "3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the fourth screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_ASPECT "3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the fourth screen; 'auto' here will try to make a best guess" }, - { OSDOPTION_RESOLUTION "3;r3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the fourth screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, - { OSDOPTION_VIEW "3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the fourth screen" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OSD PER-WINDOW VIDEO OPTIONS" }, + { OSDOPTION_SCREEN, OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the first screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_ASPECT ";screen_aspect", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio for all screens; 'auto' here will try to make a best guess" }, + { OSDOPTION_RESOLUTION ";r", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution for all screens; format is <width>x<height>[@<refreshrate>] or 'auto'" }, + { OSDOPTION_VIEW, OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for all screens" }, + + { OSDOPTION_SCREEN "0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the first screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_ASPECT "0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the first screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_RESOLUTION "0;r0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the first screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, + { OSDOPTION_VIEW "0", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the first screen" }, + + { OSDOPTION_SCREEN "1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the second screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_ASPECT "1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the second screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_RESOLUTION "1;r1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the second screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, + { OSDOPTION_VIEW "1", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the second screen" }, + + { OSDOPTION_SCREEN "2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the third screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_ASPECT "2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the third screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_RESOLUTION "2;r2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the third screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, + { OSDOPTION_VIEW "2", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the third screen" }, + + { OSDOPTION_SCREEN "3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "explicit name of the fourth screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_ASPECT "3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "aspect ratio of the fourth screen; 'auto' here will try to make a best guess" }, + { OSDOPTION_RESOLUTION "3;r3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred resolution of the fourth screen; format is <width>x<height>[@<refreshrate>] or 'auto'" }, + { OSDOPTION_VIEW "3", OSDOPTVAL_AUTO, core_options::option_type::STRING, "preferred view for the fourth screen" }, // full screen options - { nullptr, nullptr, core_options::option_type::HEADER, "OSD FULL SCREEN OPTIONS" }, - { OSDOPTION_SWITCHRES, "0", core_options::option_type::BOOLEAN, "enable resolution switching" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OSD FULL SCREEN OPTIONS" }, + { OSDOPTION_SWITCHRES, "0", core_options::option_type::BOOLEAN, "enable resolution switching" }, - { nullptr, nullptr, core_options::option_type::HEADER, "OSD ACCELERATED VIDEO OPTIONS" }, - { OSDOPTION_FILTER ";glfilter;flt", "1", core_options::option_type::BOOLEAN, "use bilinear filtering when scaling emulated video" }, - { OSDOPTION_PRESCALE "(1-8)", "1", core_options::option_type::INTEGER, "scale emulated video by this factor before applying filters/shaders" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OSD ACCELERATED VIDEO OPTIONS" }, + { OSDOPTION_FILTER ";glfilter;flt", "1", core_options::option_type::BOOLEAN, "use bilinear filtering when scaling emulated video" }, + { OSDOPTION_PRESCALE "(1-20)", "1", core_options::option_type::INTEGER, "scale emulated video by this factor before applying filters/shaders" }, #if USE_OPENGL - { nullptr, nullptr, core_options::option_type::HEADER, "OpenGL-SPECIFIC OPTIONS" }, - { OSDOPTION_GL_FORCEPOW2TEXTURE, "0", core_options::option_type::BOOLEAN, "force power-of-two texture sizes (default no)" }, - { OSDOPTION_GL_NOTEXTURERECT, "0", core_options::option_type::BOOLEAN, "don't use OpenGL GL_ARB_texture_rectangle (default on)" }, - { OSDOPTION_GL_VBO, "1", core_options::option_type::BOOLEAN, "enable OpenGL VBO if available (default on)" }, - { OSDOPTION_GL_PBO, "1", core_options::option_type::BOOLEAN, "enable OpenGL PBO if available (default on)" }, - { OSDOPTION_GL_GLSL, "0", core_options::option_type::BOOLEAN, "enable OpenGL GLSL if available (default off)" }, - { OSDOPTION_GLSL_FILTER, "1", core_options::option_type::STRING, "enable OpenGL GLSL filtering instead of FF filtering 0-plain, 1-bilinear (default), 2-bicubic" }, - { OSDOPTION_SHADER_MAME "0", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 0" }, - { OSDOPTION_SHADER_MAME "1", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 1" }, - { OSDOPTION_SHADER_MAME "2", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 2" }, - { OSDOPTION_SHADER_MAME "3", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 3" }, - { OSDOPTION_SHADER_MAME "4", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 4" }, - { OSDOPTION_SHADER_MAME "5", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 5" }, - { OSDOPTION_SHADER_MAME "6", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 6" }, - { OSDOPTION_SHADER_MAME "7", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 7" }, - { OSDOPTION_SHADER_MAME "8", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 8" }, - { OSDOPTION_SHADER_MAME "9", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 9" }, - { OSDOPTION_SHADER_SCREEN "0", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 0" }, - { OSDOPTION_SHADER_SCREEN "1", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 1" }, - { OSDOPTION_SHADER_SCREEN "2", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 2" }, - { OSDOPTION_SHADER_SCREEN "3", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 3" }, - { OSDOPTION_SHADER_SCREEN "4", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 4" }, - { OSDOPTION_SHADER_SCREEN "5", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 5" }, - { OSDOPTION_SHADER_SCREEN "6", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 6" }, - { OSDOPTION_SHADER_SCREEN "7", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 7" }, - { OSDOPTION_SHADER_SCREEN "8", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 8" }, - { OSDOPTION_SHADER_SCREEN "9", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 9" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OpenGL-SPECIFIC OPTIONS" }, + { OSDOPTION_GL_FORCEPOW2TEXTURE, "0", core_options::option_type::BOOLEAN, "force power-of-two texture sizes (default no)" }, + { OSDOPTION_GL_NOTEXTURERECT, "0", core_options::option_type::BOOLEAN, "don't use OpenGL GL_ARB_texture_rectangle (default on)" }, + { OSDOPTION_GL_VBO, "1", core_options::option_type::BOOLEAN, "enable OpenGL VBO if available (default on)" }, + { OSDOPTION_GL_PBO, "1", core_options::option_type::BOOLEAN, "enable OpenGL PBO if available (default on)" }, + { OSDOPTION_GL_GLSL, "0", core_options::option_type::BOOLEAN, "enable OpenGL GLSL if available (default off)" }, + { OSDOPTION_GLSL_FILTER, "1", core_options::option_type::STRING, "enable OpenGL GLSL filtering instead of FF filtering 0-plain, 1-bilinear (default), 2-bicubic" }, + { OSDOPTION_SHADER_MAME "0", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 0" }, + { OSDOPTION_SHADER_MAME "1", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 1" }, + { OSDOPTION_SHADER_MAME "2", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 2" }, + { OSDOPTION_SHADER_MAME "3", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 3" }, + { OSDOPTION_SHADER_MAME "4", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 4" }, + { OSDOPTION_SHADER_MAME "5", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 5" }, + { OSDOPTION_SHADER_MAME "6", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 6" }, + { OSDOPTION_SHADER_MAME "7", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 7" }, + { OSDOPTION_SHADER_MAME "8", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 8" }, + { OSDOPTION_SHADER_MAME "9", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader set mame bitmap 9" }, + { OSDOPTION_SHADER_SCREEN "0", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 0" }, + { OSDOPTION_SHADER_SCREEN "1", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 1" }, + { OSDOPTION_SHADER_SCREEN "2", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 2" }, + { OSDOPTION_SHADER_SCREEN "3", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 3" }, + { OSDOPTION_SHADER_SCREEN "4", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 4" }, + { OSDOPTION_SHADER_SCREEN "5", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 5" }, + { OSDOPTION_SHADER_SCREEN "6", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 6" }, + { OSDOPTION_SHADER_SCREEN "7", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 7" }, + { OSDOPTION_SHADER_SCREEN "8", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 8" }, + { OSDOPTION_SHADER_SCREEN "9", OSDOPTVAL_NONE, core_options::option_type::STRING, "custom OpenGL GLSL shader screen bitmap 9" }, #endif - { nullptr, nullptr, core_options::option_type::HEADER, "OSD SOUND OPTIONS" }, - { OSDOPTION_SOUND, OSDOPTVAL_AUTO, core_options::option_type::STRING, "sound output method: " }, - { OSDOPTION_AUDIO_LATENCY "(0-5)", "2", core_options::option_type::INTEGER, "set audio latency (increase to reduce glitches, decrease for responsiveness)" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OSD SOUND OPTIONS" }, + { OSDOPTION_SOUND, OSDOPTVAL_AUTO, core_options::option_type::STRING, "sound output method: " }, + { OSDOPTION_AUDIO_LATENCY "(0-5)", "2", core_options::option_type::INTEGER, "set audio latency (increase to reduce glitches, decrease for responsiveness)" }, #ifndef NO_USE_PORTAUDIO - { nullptr, nullptr, core_options::option_type::HEADER, "PORTAUDIO OPTIONS" }, - { OSDOPTION_PA_API, OSDOPTVAL_NONE, core_options::option_type::STRING, "PortAudio API" }, - { OSDOPTION_PA_DEVICE, OSDOPTVAL_NONE, core_options::option_type::STRING, "PortAudio device" }, - { OSDOPTION_PA_LATENCY "(0-0.25)", "0", core_options::option_type::FLOAT, "suggested latency in seconds, 0 for default" }, + { nullptr, nullptr, core_options::option_type::HEADER, "PORTAUDIO OPTIONS" }, + { OSDOPTION_PA_API, OSDOPTVAL_NONE, core_options::option_type::STRING, "PortAudio API" }, + { OSDOPTION_PA_DEVICE, OSDOPTVAL_NONE, core_options::option_type::STRING, "PortAudio device" }, + { OSDOPTION_PA_LATENCY "(0-0.25)", "0", core_options::option_type::FLOAT, "suggested latency in seconds, 0 for default" }, #endif #ifdef SDLMAME_MACOSX - { nullptr, nullptr, core_options::option_type::HEADER, "CoreAudio-SPECIFIC OPTIONS" }, - { OSDOPTION_AUDIO_OUTPUT, OSDOPTVAL_AUTO, core_options::option_type::STRING, "audio output device" }, - { OSDOPTION_AUDIO_EFFECT "0", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 0" }, - { OSDOPTION_AUDIO_EFFECT "1", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 1" }, - { OSDOPTION_AUDIO_EFFECT "2", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 2" }, - { OSDOPTION_AUDIO_EFFECT "3", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 3" }, - { OSDOPTION_AUDIO_EFFECT "4", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 4" }, - { OSDOPTION_AUDIO_EFFECT "5", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 5" }, - { OSDOPTION_AUDIO_EFFECT "6", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 6" }, - { OSDOPTION_AUDIO_EFFECT "7", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 7" }, - { OSDOPTION_AUDIO_EFFECT "8", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 8" }, - { OSDOPTION_AUDIO_EFFECT "9", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 9" }, + { nullptr, nullptr, core_options::option_type::HEADER, "CoreAudio-SPECIFIC OPTIONS" }, + { OSDOPTION_AUDIO_OUTPUT, OSDOPTVAL_AUTO, core_options::option_type::STRING, "audio output device" }, + { OSDOPTION_AUDIO_EFFECT "0", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 0" }, + { OSDOPTION_AUDIO_EFFECT "1", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 1" }, + { OSDOPTION_AUDIO_EFFECT "2", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 2" }, + { OSDOPTION_AUDIO_EFFECT "3", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 3" }, + { OSDOPTION_AUDIO_EFFECT "4", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 4" }, + { OSDOPTION_AUDIO_EFFECT "5", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 5" }, + { OSDOPTION_AUDIO_EFFECT "6", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 6" }, + { OSDOPTION_AUDIO_EFFECT "7", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 7" }, + { OSDOPTION_AUDIO_EFFECT "8", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 8" }, + { OSDOPTION_AUDIO_EFFECT "9", OSDOPTVAL_NONE, core_options::option_type::STRING, "AudioUnit effect 9" }, #endif - { nullptr, nullptr, core_options::option_type::HEADER, "BGFX POST-PROCESSING OPTIONS" }, - { OSDOPTION_BGFX_PATH, "bgfx", core_options::option_type::STRING, "path to BGFX-related files" }, - { OSDOPTION_BGFX_BACKEND, "auto", core_options::option_type::STRING, "BGFX backend to use (d3d9, d3d11, d3d12, metal, opengl, gles, vulkan)" }, - { OSDOPTION_BGFX_DEBUG, "0", core_options::option_type::BOOLEAN, "enable BGFX debugging statistics" }, - { OSDOPTION_BGFX_SCREEN_CHAINS, "default", core_options::option_type::STRING, "comma-delimited list of screen chain JSON names, colon-delimited per-window" }, - { OSDOPTION_BGFX_SHADOW_MASK, "slot-mask.png", core_options::option_type::STRING, "shadow mask texture name" }, - { OSDOPTION_BGFX_LUT, "lut-default.png", core_options::option_type::STRING, "LUT texture name" }, - { OSDOPTION_BGFX_AVI_NAME, OSDOPTVAL_AUTO, core_options::option_type::STRING, "filename for BGFX output logging" }, + { nullptr, nullptr, core_options::option_type::HEADER, "OSD MIDI OPTIONS" }, + { OSDOPTION_MIDI_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "MIDI I/O method: " }, + + { nullptr, nullptr, core_options::option_type::HEADER, "OSD EMULATED NETWORKING OPTIONS" }, + { OSDOPTION_NETWORK_PROVIDER, OSDOPTVAL_AUTO, core_options::option_type::STRING, "Emulated networking provider: " }, - // End of list + { nullptr, nullptr, core_options::option_type::HEADER, "BGFX POST-PROCESSING OPTIONS" }, + { OSDOPTION_BGFX_PATH, "bgfx", core_options::option_type::PATH, "path to BGFX-related files" }, + { OSDOPTION_BGFX_BACKEND, "auto", core_options::option_type::STRING, "BGFX backend to use (d3d9, d3d11, d3d12, metal, opengl, gles, vulkan)" }, + { OSDOPTION_BGFX_DEBUG, "0", core_options::option_type::BOOLEAN, "enable BGFX debugging statistics" }, + { OSDOPTION_BGFX_SCREEN_CHAINS, "", core_options::option_type::STRING, "comma-delimited list of screen chain JSON names, colon-delimited per-window" }, + { OSDOPTION_BGFX_SHADOW_MASK, "slot-mask.png", core_options::option_type::STRING, "shadow mask texture name" }, + { OSDOPTION_BGFX_LUT, "lut-default.png", core_options::option_type::STRING, "LUT texture name" }, + { OSDOPTION_BGFX_AVI_NAME, OSDOPTVAL_AUTO, core_options::option_type::PATH, "filename for BGFX output logging" }, + + // End of list { nullptr } }; @@ -182,7 +193,7 @@ osd_options::osd_options() } // Window list -std::list<std::shared_ptr<osd_window>> osd_common_t::s_window_list; +std::list<std::unique_ptr<osd_window> > osd_common_t::s_window_list; //------------------------------------------------- // osd_interface - constructor @@ -217,7 +228,7 @@ osd_common_t::~osd_common_t() osd_output::pop(this); } -#define REGISTER_MODULE(_O, _X ) { extern const module_type _X; _O . register_module( _X ); } +#define REGISTER_MODULE(O, X) { extern const module_type X; O.register_module(X); } void osd_common_t::register_options() { @@ -227,6 +238,26 @@ void osd_common_t::register_options() REGISTER_MODULE(m_mod_man, FONT_SDL); REGISTER_MODULE(m_mod_man, FONT_NONE); +#if defined(SDLMAME_EMSCRIPTEN) + REGISTER_MODULE(m_mod_man, RENDERER_SDL1); // don't bother trying to use video acceleration in browsers +#endif +#if defined(OSD_WINDOWS) + REGISTER_MODULE(m_mod_man, RENDERER_D3D); // this is only built for OSD=windows, there's no dummy stub +#endif +#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) + REGISTER_MODULE(m_mod_man, RENDERER_BGFX); // try BGFX before GDI on windows to get DirectX 10/11 acceleration +#endif + REGISTER_MODULE(m_mod_man, RENDERER_GDI); // GDI ahead of OpenGL as there's a chance Windows has no OpenGL + REGISTER_MODULE(m_mod_man, RENDERER_OPENGL); +#if !defined(OSD_WINDOWS) && !defined(SDLMAME_WIN32) + REGISTER_MODULE(m_mod_man, RENDERER_BGFX); // try BGFX after OpenGL on other operating systems for now +#endif + REGISTER_MODULE(m_mod_man, RENDERER_SDL2); +#if !defined(SDLMAME_EMSCRIPTEN) + REGISTER_MODULE(m_mod_man, RENDERER_SDL1); +#endif + REGISTER_MODULE(m_mod_man, RENDERER_NONE); + REGISTER_MODULE(m_mod_man, SOUND_DSOUND); REGISTER_MODULE(m_mod_man, SOUND_XAUDIO2); REGISTER_MODULE(m_mod_man, SOUND_COREAUDIO); @@ -282,7 +313,8 @@ void osd_common_t::register_options() REGISTER_MODULE(m_mod_man, LIGHTGUNINPUT_WIN32); REGISTER_MODULE(m_mod_man, LIGHTGUN_NONE); - REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_SDL); + REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_SDLGAME); + REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_SDLJOY); REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_WINHYBRID); REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_DINPUT); REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_XINPUT); @@ -295,95 +327,34 @@ void osd_common_t::register_options() // after initialization we know which modules are supported - - const char *names[20]; - int num; - std::vector<const char *> dnames; - - m_mod_man.get_module_names(OSD_MONITOR_PROVIDER, 20, num, names); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_MONITOR_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_FONT_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_FONT_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_KEYBOARDINPUT_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_KEYBOARDINPUT_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_MOUSEINPUT_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_MOUSEINPUT_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_LIGHTGUNINPUT_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_LIGHTGUNINPUT_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_JOYSTICKINPUT_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_JOYSTICKINPUT_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_SOUND_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_SOUND_PROVIDER, dnames); - -#if 0 - // Register midi options and update options - m_mod_man.get_module_names(OSD_MIDI_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_MIDI_PROVIDER, dnames); -#endif - - // Register debugger options and update options - m_mod_man.get_module_names(OSD_DEBUG_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_DEBUG_PROVIDER, dnames); - - m_mod_man.get_module_names(OSD_OUTPUT_PROVIDER, 20, num, names); - dnames.clear(); - for (int i = 0; i < num; i++) - dnames.push_back(names[i]); - update_option(OSD_OUTPUT_PROVIDER, dnames); - - // Register video options and update options - video_options_add("none", nullptr); - video_register(); - update_option(OSDOPTION_VIDEO, m_video_names); -} - -void osd_common_t::update_option(const std::string &key, std::vector<const char *> &values) + update_option(OSD_FONT_PROVIDER, m_mod_man.get_module_names(OSD_FONT_PROVIDER)); + update_option(OSD_RENDERER_PROVIDER, m_mod_man.get_module_names(OSD_RENDERER_PROVIDER)); + update_option(OSD_SOUND_PROVIDER, m_mod_man.get_module_names(OSD_SOUND_PROVIDER)); + update_option(OSD_MONITOR_PROVIDER, m_mod_man.get_module_names(OSD_MONITOR_PROVIDER)); + update_option(OSD_KEYBOARDINPUT_PROVIDER, m_mod_man.get_module_names(OSD_KEYBOARDINPUT_PROVIDER)); + update_option(OSD_MOUSEINPUT_PROVIDER, m_mod_man.get_module_names(OSD_MOUSEINPUT_PROVIDER)); + update_option(OSD_LIGHTGUNINPUT_PROVIDER, m_mod_man.get_module_names(OSD_LIGHTGUNINPUT_PROVIDER)); + update_option(OSD_JOYSTICKINPUT_PROVIDER, m_mod_man.get_module_names(OSD_JOYSTICKINPUT_PROVIDER)); + update_option(OSD_MIDI_PROVIDER, m_mod_man.get_module_names(OSD_MIDI_PROVIDER)); + update_option(OSD_NETDEV_PROVIDER, m_mod_man.get_module_names(OSD_NETDEV_PROVIDER)); + update_option(OSD_DEBUG_PROVIDER, m_mod_man.get_module_names(OSD_DEBUG_PROVIDER)); + update_option(OSD_OUTPUT_PROVIDER, m_mod_man.get_module_names(OSD_OUTPUT_PROVIDER)); +} + +void osd_common_t::update_option(const std::string &key, std::vector<std::string_view> const &values) { std::string current_value(m_options.description(key.c_str())); std::string new_option_value(""); for (unsigned int index = 0; index < values.size(); index++) { - std::string t(values[index]); if (new_option_value.length() > 0) { - if( index != (values.size()-1)) + if (index != (values.size() - 1)) new_option_value.append(", "); else new_option_value.append(" or "); } - new_option_value.append(t); + new_option_value.append(values[index]); } m_option_descs[key] = current_value + new_option_value; @@ -394,7 +365,7 @@ void osd_common_t::update_option(const std::string &key, std::vector<const char //------------------------------------------------- // output_callback - callback for osd_printf_... //------------------------------------------------- -void osd_common_t::output_callback(osd_output_channel channel, const util::format_argument_pack<std::ostream> &args) +void osd_common_t::output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args) { switch (channel) { @@ -620,28 +591,50 @@ bool osd_common_t::execute_command(const char *command) { if (strcmp(command, OSDCOMMAND_LIST_NETWORK_ADAPTERS) == 0) { - osd_module *om = select_module_options(options(), OSD_NETDEV_PROVIDER); - - if (om->probe()) + osd_module &om = select_module_options<osd_module>(OSD_NETDEV_PROVIDER); + auto const &interfaces = get_netdev_list(); + if (interfaces.empty()) + { + printf("No supported network interfaces were found\n"); + } + else { - om->init(options()); - osd_list_network_adapters(); - om->exit(); + printf("Available network interfaces:\n"); + for (auto &entry : interfaces) + { + printf(" %s\n", entry->description); + } } + om.exit(); return true; } else if (strcmp(command, OSDCOMMAND_LIST_MIDI_DEVICES) == 0) { - osd_module *om = select_module_options(options(), OSD_MIDI_PROVIDER); - auto *pm = select_module_options<midi_module *>(options(), OSD_MIDI_PROVIDER); - - if (om->probe()) + osd_module &om = select_module_options<osd_module>(OSD_MIDI_PROVIDER); + auto const ports = dynamic_cast<midi_module &>(om).list_midi_ports(); + if (ports.empty()) { - om->init(options()); - pm->list_midi_devices(); - om->exit(); + 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()); + } + } + om.exit(); + return true; } @@ -657,10 +650,10 @@ static void output_notifier_callback(const char *outname, int32_t value, void *p void osd_common_t::init_subsystems() { // monitors have to be initialized before video init - m_monitor_module = select_module_options<monitor_module *>(options(), OSD_MONITOR_PROVIDER); - assert(m_monitor_module != nullptr); - m_monitor_module->init(options()); + m_monitor_module = &select_module_options<monitor_module>(OSD_MONITOR_PROVIDER); + // various modules depend on having a window handle + m_render = &select_module_options<render_module>(OSD_RENDERER_PROVIDER); if (!video_init()) { video_exit(); @@ -670,32 +663,24 @@ void osd_common_t::init_subsystems() exit(-1); } - m_keyboard_input = select_module_options<input_module *>(options(), OSD_KEYBOARDINPUT_PROVIDER); - m_mouse_input = select_module_options<input_module *>(options(), OSD_MOUSEINPUT_PROVIDER); - m_lightgun_input = select_module_options<input_module *>(options(), OSD_LIGHTGUNINPUT_PROVIDER); - m_joystick_input = select_module_options<input_module *>(options(), OSD_JOYSTICKINPUT_PROVIDER); + m_keyboard_input = &select_module_options<input_module>(OSD_KEYBOARDINPUT_PROVIDER); + m_mouse_input = &select_module_options<input_module>(OSD_MOUSEINPUT_PROVIDER); + m_lightgun_input = &select_module_options<input_module>(OSD_LIGHTGUNINPUT_PROVIDER); + m_joystick_input = &select_module_options<input_module>(OSD_JOYSTICKINPUT_PROVIDER); - m_font_module = select_module_options<font_module *>(options(), OSD_FONT_PROVIDER); - m_sound = select_module_options<sound_module *>(options(), OSD_SOUND_PROVIDER); - m_sound->m_sample_rate = options().sample_rate(); - m_sound->m_audio_latency = options().audio_latency(); + m_font_module = &select_module_options<font_module>(OSD_FONT_PROVIDER); + m_sound = &select_module_options<sound_module>(OSD_SOUND_PROVIDER); - m_debugger = select_module_options<debug_module *>(options(), OSD_DEBUG_PROVIDER); + m_debugger = &select_module_options<debug_module>(OSD_DEBUG_PROVIDER); - select_module_options<netdev_module *>(options(), OSD_NETDEV_PROVIDER); + select_module_options<netdev_module>(OSD_NETDEV_PROVIDER); - m_midi = select_module_options<midi_module *>(options(), OSD_MIDI_PROVIDER); + m_midi = &select_module_options<midi_module>(OSD_MIDI_PROVIDER); - m_output = select_module_options<output_module *>(options(), OSD_OUTPUT_PROVIDER); - m_output->set_machine(&machine()); + m_output = &select_module_options<output_module>(OSD_OUTPUT_PROVIDER); machine().output().set_global_notifier(output_notifier_callback, this); - m_mod_man.init(options()); - input_init(); - // we need pause callbacks - machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&osd_common_t::input_pause, this)); - machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&osd_common_t::input_resume, this)); } bool osd_common_t::video_init() @@ -713,10 +698,6 @@ bool osd_common_t::no_sound() return (strcmp(options().sound(),"none")==0) ? true : false; } -void osd_common_t::video_register() -{ -} - bool osd_common_t::input_init() { m_keyboard_input->input_init(machine()); @@ -726,20 +707,12 @@ bool osd_common_t::input_init() return true; } -void osd_common_t::input_pause() -{ - m_keyboard_input->pause(); - m_mouse_input->pause(); - m_lightgun_input->pause(); - m_joystick_input->pause(); -} - -void osd_common_t::input_resume() +void osd_common_t::poll_input_modules(bool relative_reset) { - m_keyboard_input->resume(); - m_mouse_input->resume(); - m_lightgun_input->resume(); - m_joystick_input->resume(); + m_keyboard_input->poll_if_necessary(relative_reset); + m_mouse_input->poll_if_necessary(relative_reset); + m_lightgun_input->poll_if_necessary(relative_reset); + m_joystick_input->poll_if_necessary(relative_reset); } void osd_common_t::exit_subsystems() @@ -757,17 +730,15 @@ void osd_common_t::window_exit() void osd_common_t::osd_exit() { + // destroy the renderers before shutting down their parent module + for (auto it = window_list().rbegin(); window_list().rend() != it; ++it) + (*it)->renderer_reset(); + m_mod_man.exit(); exit_subsystems(); } -void osd_common_t::video_options_add(const char *name, void *type) -{ - //m_video_options.add(name, type, false); - m_video_names.push_back(name); -} - osd_font::ptr osd_common_t::font_alloc() { return m_font_module->font_alloc(); @@ -778,7 +749,12 @@ 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_midi_device(); + return m_midi->create_output(name); } diff --git a/src/osd/modules/lib/osdobj_common.h b/src/osd/modules/lib/osdobj_common.h index 55ed36e5060..919bbc4f717 100644 --- a/src/osd/modules/lib/osdobj_common.h +++ b/src/osd/modules/lib/osdobj_common.h @@ -2,30 +2,42 @@ // copyright-holders:Aaron Giles /*************************************************************************** - osdepend.h + osdobj_common.h OS-dependent code interface. *******************************************************************c********/ - -#pragma once - #ifndef MAME_OSD_LIB_OSDOBJ_COMMON_H #define MAME_OSD_LIB_OSDOBJ_COMMON_H +#pragma once + #include "osdcore.h" #include "osdepend.h" + #include "modules/osdmodule.h" #include "modules/output/output_module.h" + #include "emuopts.h" + #include "strformat.h" + +#include <iosfwd> #include <list> +#include <memory> +#include <string> +#include <string_view> +#include <unordered_map> +#include <vector> + //============================================================ // Defines //============================================================ #define OSDOPTION_UIMODEKEY "uimodekey" +#define OSDOPTION_CONTROLLER_MAP_FILE "controller_map" +#define OSDOPTION_BACKGROUND_INPUT "background_input" #define OSDCOMMAND_LIST_MIDI_DEVICES "listmidi" #define OSDCOMMAND_LIST_NETWORK_ADAPTERS "listnetwork" @@ -75,8 +87,9 @@ #define OSDOPTION_AUDIO_OUTPUT "audio_output" #define OSDOPTION_AUDIO_EFFECT "audio_effect" -#define OSDOPTVAL_AUTO "auto" -#define OSDOPTVAL_NONE "none" +#define OSDOPTION_MIDI_PROVIDER "midiprovider" + +#define OSDOPTION_NETWORK_PROVIDER "networkprovider" #define OSDOPTION_BGFX_PATH "bgfx_path" #define OSDOPTION_BGFX_BACKEND "bgfx_backend" @@ -86,6 +99,9 @@ #define OSDOPTION_BGFX_LUT "bgfx_lut" #define OSDOPTION_BGFX_AVI_NAME "bgfx_avi_name" +#define OSDOPTVAL_AUTO "auto" +#define OSDOPTVAL_NONE "none" + //============================================================ // TYPE DEFINITIONS //============================================================ @@ -98,6 +114,8 @@ public: // keyboard mapping const char *ui_mode_key() const { return value(OSDOPTION_UIMODEKEY); } + const char *controller_mapping_file() const { return value(OSDOPTION_CONTROLLER_MAP_FILE); } + bool background_input() const { return bool_value(OSDOPTION_BACKGROUND_INPUT); } // debugging options const char *debugger() const { return value(OSDOPTION_DEBUGGER); } @@ -165,22 +183,23 @@ public: // PortAudio options const char *pa_api() const { return value(OSDOPTION_PA_API); } const char *pa_device() const { return value(OSDOPTION_PA_DEVICE); } - const float pa_latency() const { return float_value(OSDOPTION_PA_LATENCY); } + float pa_latency() const { return float_value(OSDOPTION_PA_LATENCY); } static const options_entry s_option_entries[]; }; // ======================> osd_interface -class font_module; -class sound_module; class debug_module; -class midi_module; +class font_module; class input_module; -class output_module; +class midi_module; class monitor_module; class osd_watchdog; class osd_window; +class output_module; +class render_module; +class sound_module; // description of the currently-running machine class osd_common_t : public osd_interface, osd_output @@ -219,7 +238,8 @@ public: 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; - virtual std::unique_ptr<osd_midi_device> create_midi_device() override; + 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; // FIXME: everything below seems to be osd specific and not part of // this INTERFACE but part of the osd IMPLEMENTATION @@ -232,37 +252,38 @@ public: virtual void init_subsystems(); virtual bool video_init(); - virtual void video_register(); virtual bool window_init(); - virtual void input_resume(); - virtual void exit_subsystems(); virtual void video_exit(); virtual void window_exit(); virtual void osd_exit(); - virtual void video_options_add(const char *name, void *type); - virtual osd_options &options() { return m_options; } // osd_output interface ... - virtual void output_callback(osd_output_channel channel, const util::format_argument_pack<std::ostream> &args) override; + virtual void output_callback(osd_output_channel channel, const util::format_argument_pack<char> &args) override; bool verbose() const { return m_print_verbose; } virtual void set_verbose(bool print_verbose) override { m_print_verbose = print_verbose; } void notify(const char *outname, int32_t value) const { m_output->notify(outname, value); } - static std::list<std::shared_ptr<osd_window>> s_window_list; + virtual void process_events() = 0; + virtual bool has_focus() const = 0; + + static const std::list<std::unique_ptr<osd_window> > &window_list() { return s_window_list; } protected: virtual bool input_init(); - virtual void input_pause(); 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; + private: // internal state running_machine * m_machine; @@ -273,28 +294,26 @@ private: osd_module_manager m_mod_man; font_module *m_font_module; - void update_option(const std::string &key, std::vector<const char *> &values); + void update_option(const std::string &key, std::vector<std::string_view> const &values); // FIXME: should be elsewhere - osd_module *select_module_options(const core_options &opts, const std::string &opt_name) + template<class C> + C &select_module_options(const std::string &opt_name) { - std::string opt_val = opts.exists(opt_name) ? opts.value(opt_name) : ""; - if (opt_val.compare("auto")==0) + std::string opt_val = options().exists(opt_name) ? options().value(opt_name) : ""; + if (opt_val == "auto") + { opt_val = ""; + } else if (!m_mod_man.type_has_name(opt_name.c_str(), opt_val.c_str())) { osd_printf_warning("Value %s not supported for option %s - falling back to auto\n", opt_val, opt_name); opt_val = ""; } - return m_mod_man.select_module(opt_name.c_str(), opt_val.c_str()); - } - - template<class C> - C select_module_options(const core_options &opts, const std::string &opt_name) - { - return dynamic_cast<C>(select_module_options(opts, opt_name)); + return m_mod_man.select_module<C>(*this, options(), opt_name.c_str(), opt_val.c_str()); } protected: + render_module* m_render; sound_module* m_sound; debug_module* m_debugger; midi_module* m_midi; @@ -308,7 +327,6 @@ protected: std::vector<ui::menu_item> m_sliders; private: - std::vector<const char *> m_video_names; std::unordered_map<std::string, std::string> m_option_descs; }; diff --git a/src/osd/modules/midi/midi_module.h b/src/osd/modules/midi/midi_module.h index 8e64800622e..6ea7290805b 100644 --- a/src/osd/modules/midi/midi_module.h +++ b/src/osd/modules/midi/midi_module.h @@ -9,10 +9,12 @@ #pragma once -#include "osdepend.h" -#include "modules/osdmodule.h" +#include "interface/midiport.h" #include <memory> +#include <string> +#include <string_view> +#include <vector> //============================================================ @@ -24,12 +26,23 @@ class midi_module { public: - virtual ~midi_module() { } + struct port_info + { + std::string name; + bool input; + bool output; + bool default_input; + bool default_output; + }; + using port_info_vector = std::vector<port_info>; + + virtual ~midi_module() = default; + // 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 port_info_vector 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 21837d1be3c..c3dd0aaadc5 100644 --- a/src/osd/modules/midi/none.cpp +++ b/src/osd/modules/midi/none.cpp @@ -6,87 +6,36 @@ Empty shim for systems not supporting midi / portmidi -*******************************************************************c********/ +***************************************************************************/ -#include "osdcore.h" -#include "modules/osdmodule.h" #include "midi_module.h" -class none_module : public osd_module, public midi_module -{ -public: +#include "modules/osdmodule.h" +#include "osdcore.h" - none_module() : osd_module(OSD_MIDI_PROVIDER, "pm"), midi_module() - { - } - virtual ~none_module() { } +#include <memory> - virtual int init(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; -}; +namespace osd { +namespace { -class osd_midi_device_none : public osd_midi_device +class none_module : public osd_module, public midi_module { public: - virtual ~osd_midi_device_none() { } - virtual bool open_input(const char *devname) override; - virtual bool open_output(const char *devname) override; - virtual void close() override; - virtual bool poll() override; - virtual int read(uint8_t *pOut) override; - virtual void write(uint8_t data) override; -}; + none_module() : osd_module(OSD_MIDI_PROVIDER, "none"), midi_module() { } -std::unique_ptr<osd_midi_device> none_module::create_midi_device() -{ - return std::make_unique<osd_midi_device_none>(); -} - - -int none_module::init(const osd_options &options) -{ - return 0; -} + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } + virtual void exit() override { } -void none_module::exit() -{ -} - -void none_module::list_midi_devices() -{ - osd_printf_warning("\nMIDI is not supported in this build\n"); -} - -bool osd_midi_device_none::open_input(const char *devname) -{ - return false; -} - -bool osd_midi_device_none::open_output(const char *devname) -{ - return false; -} - -void osd_midi_device_none::close() -{ -} + 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 port_info_vector list_midi_ports() override { return port_info_vector(); } +}; -bool osd_midi_device_none::poll() -{ - return false; -} +} // anonymous namespace -int osd_midi_device_none::read(uint8_t *pOut) -{ - return 0; -} +} // namespace osd -void osd_midi_device_none::write(uint8_t data) -{ -} -MODULE_DEFINITION(MIDI_NONE, none_module) +MODULE_DEFINITION(MIDI_NONE, osd::none_module) diff --git a/src/osd/modules/midi/portmidi.cpp b/src/osd/modules/midi/portmidi.cpp index 9cb5ddac39b..3a1dbf6739d 100644 --- a/src/osd/modules/midi/portmidi.cpp +++ b/src/osd/modules/midi/portmidi.cpp @@ -2,18 +2,30 @@ // copyright-holders:R. Belmont /*************************************************************************** - portmap.c + portmidi.cpp Midi implementation based on portmidi library *******************************************************************c********/ +#include "midi_module.h" + +#include "modules/osdmodule.h" + #ifndef NO_USE_MIDI +#include "interface/midiport.h" +#include "osdcore.h" // osd_printf_* + #include <portmidi.h> -#include "osdcore.h" -#include "modules/osdmodule.h" -#include "midi_module.h" + +#include <cstdio> +#include <cstring> + + +namespace osd { + +namespace { class pm_module : public osd_module, public midi_module { @@ -24,48 +36,54 @@ public: } virtual ~pm_module() { } - virtual int init(const osd_options &options)override; - virtual void exit()override; + 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 port_info_vector 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(const osd_options &options) + +int pm_module::init(osd_interface &osd, const osd_options &options) { + // FIXME: check error return code Pm_Initialize(); return 0; } @@ -75,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)) - { - found_dev = Pm_GetDefaultInputDeviceID(); - } - else + PortMidiStream *stream = nullptr; + PmError const err = Pm_OpenInput(&stream, found_dev, nullptr, RX_EVENT_BUF_SIZE, nullptr, nullptr); + if (pmNoError != err) { - 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 false; + return std::make_unique<midi_output_pm>(stream); + } + catch (...) + { + Pm_Close(stream); + return nullptr; } } -void osd_midi_device_pm::close() +midi_module::port_info_vector 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(); + port_info_vector result; + result.reserve(num_devs); + for (int i = 0; num_devs > i; ++i) + { + auto const pm_info = Pm_GetDeviceInfo(i); + result.emplace_back(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) @@ -229,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 @@ -254,7 +247,7 @@ int osd_midi_device_pm::read(uint8_t *pOut) bytesOut++; if (status == MIDI_EOX) { - rx_sysex = false; + m_rx_sysex = false; } } } @@ -262,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; } } } @@ -281,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; @@ -291,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; @@ -327,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; } @@ -338,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; } } @@ -396,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: @@ -430,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; @@ -455,20 +455,24 @@ 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; } } + +} // anonymous namespace + +} // namespace osd + #else - #include "modules/osdmodule.h" - #include "midi_module.h" - MODULE_NOT_SUPPORTED(pm_module, OSD_MIDI_PROVIDER, "pm") +namespace osd { namespace { MODULE_NOT_SUPPORTED(pm_module, OSD_MIDI_PROVIDER, "pm") } } + #endif -MODULE_DEFINITION(MIDI_PM, pm_module) +MODULE_DEFINITION(MIDI_PM, osd::pm_module) diff --git a/src/osd/modules/monitor/monitor_common.cpp b/src/osd/modules/monitor/monitor_common.cpp index baa80ed7f66..f6a338d327c 100644 --- a/src/osd/modules/monitor/monitor_common.cpp +++ b/src/osd/modules/monitor/monitor_common.cpp @@ -96,7 +96,7 @@ float monitor_module_base::get_aspect(const char* defdata, const char* data, int return float(num) / float(den); } -int monitor_module_base::init(const osd_options& options) +int monitor_module_base::init(osd_interface &osd, const osd_options &options) { if (!m_initialized) { diff --git a/src/osd/modules/monitor/monitor_common.h b/src/osd/modules/monitor/monitor_common.h index c8257c36e74..4b1d17d2f1f 100644 --- a/src/osd/modules/monitor/monitor_common.h +++ b/src/osd/modules/monitor/monitor_common.h @@ -4,11 +4,17 @@ * monitor_common.h * */ -#ifndef __MONITOR_COMMON_H__ -#define __MONITOR_COMMON_H__ +#ifndef MAME_OSD_MONITOR_MONITOR_COMMON_H +#define MAME_OSD_MONITOR_MONITOR_COMMON_H + +#pragma once #include "monitor_module.h" + +#include <cstdint> #include <map> +#include <memory> + //============================================================ // monitor_module_base @@ -16,32 +22,30 @@ class monitor_module_base : public monitor_module { -private: - std::map<std::uint64_t, std::shared_ptr<osd_monitor_info>> m_monitor_index; - -protected: - bool m_initialized; - public: - monitor_module_base(const char* type, const char* name) - : monitor_module(type, name), - m_initialized(false) + monitor_module_base(const char* type, const char* name) : + monitor_module(type, name), + m_initialized(false) { } std::shared_ptr<osd_monitor_info> pick_monitor(osd_options& options, int index) override; std::shared_ptr<osd_monitor_info> monitor_from_handle(std::uint64_t handle) override; - int init(const osd_options& options) override; + int init(osd_interface &osd, const osd_options& options) override; void exit() override; protected: - virtual int init_internal(const osd_options& options) = 0; + virtual int init_internal(const osd_options &options) = 0; void add_monitor(std::shared_ptr<osd_monitor_info> monitor); + bool m_initialized; + private: - std::shared_ptr<osd_monitor_info> pick_monitor_internal(osd_options& options, int index); static float get_aspect(const char *defdata, const char *data, int report_error); + + std::map<std::uint64_t, std::shared_ptr<osd_monitor_info> > m_monitor_index; + std::shared_ptr<osd_monitor_info> pick_monitor_internal(osd_options& options, int index); }; -#endif +#endif // MAME_OSD_MONITOR_MONITOR_COMMON_H diff --git a/src/osd/modules/monitor/monitor_module.h b/src/osd/modules/monitor/monitor_module.h index 3244e7fd42b..38e7e02fec2 100644 --- a/src/osd/modules/monitor/monitor_module.h +++ b/src/osd/modules/monitor/monitor_module.h @@ -8,6 +8,7 @@ #ifndef MAME_OSD_MODULES_MONITOR_MONITORMODULE_H #define MAME_OSD_MODULES_MONITOR_MONITORMODULE_H +#include <cstdint> #include <vector> #include "modules/osdmodule.h" @@ -53,7 +54,7 @@ public: float aspect() const { return m_aspect; } float pixel_aspect() const { return m_aspect / (float(m_pos_size.width()) / float(m_pos_size.height())); } - void update_resolution(const int new_width, const int new_height) const { m_pos_size.resize(new_width, new_height); } + void update_resolution(const int new_width, const int new_height) { m_pos_size = m_pos_size.resize(new_width, new_height); } void set_aspect(const float a) { m_aspect = a; } bool is_primary() const { return m_is_primary; } diff --git a/src/osd/modules/monitor/monitor_sdl.cpp b/src/osd/modules/monitor/monitor_sdl.cpp index 13f1b847693..679c7a4f97d 100644 --- a/src/osd/modules/monitor/monitor_sdl.cpp +++ b/src/osd/modules/monitor/monitor_sdl.cpp @@ -10,14 +10,19 @@ #if defined(OSD_SDL) -#include <algorithm> -#include <SDL2/SDL.h> +#include "monitor_common.h" +#include "modules/lib/osdobj_common.h" #include "modules/osdwindow.h" -#include "monitor_common.h" + #include "osdcore.h" #include "window.h" +#include <SDL2/SDL.h> + +#include <algorithm> + + inline osd_rect SDL_Rect_to_osd_rect(const SDL_Rect &r) { return osd_rect(r.x, r.y, r.w, r.h); diff --git a/src/osd/modules/netdev/netdev_module.h b/src/osd/modules/netdev/netdev_module.h index b1d537cacf5..b102e56fbb8 100644 --- a/src/osd/modules/netdev/netdev_module.h +++ b/src/osd/modules/netdev/netdev_module.h @@ -4,25 +4,23 @@ * netdev_module.h * */ +#ifndef MAME_OSD_NETDEV_NETDEV_MODULE_H +#define MAME_OSD_NETDEV_NETDEV_MODULE_H -#ifndef NETDEV_MODULE_H_ -#define NETDEV_MODULE_H_ +#pragma once -#include "osdepend.h" -#include "modules/osdmodule.h" //============================================================ // CONSTANTS //============================================================ -#define OSD_NETDEV_PROVIDER "netdevprovider" +#define OSD_NETDEV_PROVIDER "networkprovider" class netdev_module { public: - virtual ~netdev_module() { } + virtual ~netdev_module() = default; // no specific routines below ... may change }; - -#endif /* NETDEV_MODULE_H_ */ +#endif // MAME_OSD_NETDEV_NETDEV_MODULE_H diff --git a/src/osd/modules/netdev/none.cpp b/src/osd/modules/netdev/none.cpp index a5b520f26a5..8441c002802 100644 --- a/src/osd/modules/netdev/none.cpp +++ b/src/osd/modules/netdev/none.cpp @@ -4,20 +4,28 @@ * none.c * */ - #include "netdev_module.h" + #include "modules/osdmodule.h" + +namespace osd { + +namespace { + class netdev_none : public osd_module, public netdev_module { public: - netdev_none() - : osd_module(OSD_NETDEV_PROVIDER, "none"), netdev_module() + netdev_none() : osd_module(OSD_NETDEV_PROVIDER, "none"), netdev_module() { } virtual ~netdev_none() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } }; -MODULE_DEFINITION(NETDEV_NONE, netdev_none) +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(NETDEV_NONE, osd::netdev_none) diff --git a/src/osd/modules/netdev/pcap.cpp b/src/osd/modules/netdev/pcap.cpp index 2ae32df0f3b..77ca557f1de 100644 --- a/src/osd/modules/netdev/pcap.cpp +++ b/src/osd/modules/netdev/pcap.cpp @@ -1,13 +1,17 @@ // license:BSD-3-Clause // copyright-holders:Carl -#if defined(OSD_NET_USE_PCAP) - -#include "emu.h" -#include "osdnet.h" #include "netdev_module.h" + #include "modules/osdmodule.h" + +#if defined(OSD_NET_USE_PCAP) + #include "modules/lib/osdlib.h" +#include "osdnet.h" +#include "osdcore.h" // osd_printf_* + +#include "util/strformat.h" // string_format #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) #include <windows.h> @@ -25,6 +29,10 @@ #include <pcap.h> +namespace osd { + +namespace { + // Typedefs for dynamically loaded functions typedef int (*pcap_findalldevs_fn)(pcap_if_t **, char *); typedef pcap_t *(*pcap_open_live_fn)(const char *, int, int, int, char *); @@ -48,7 +56,7 @@ public: virtual ~pcap_module() { } - virtual int init(const osd_options &options) override; + virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; virtual bool probe() override @@ -93,7 +101,8 @@ public: static pcap_module *module = nullptr; #ifdef SDLMAME_MACOSX -struct netdev_pcap_context { +struct netdev_pcap_context +{ uint8_t *pkt; int len; pcap_t *p; @@ -105,16 +114,18 @@ struct netdev_pcap_context { }; #endif -class netdev_pcap : public osd_netdev +class netdev_pcap : public osd_network_device { public: - netdev_pcap(const char *name, class device_network_interface *ifdev, int rate); + netdev_pcap(const char *name, network_handler &ifdev); ~netdev_pcap(); virtual int send(uint8_t *buf, int len) override; - virtual void set_mac(const char *mac) override; + virtual void set_mac(const uint8_t *mac) override; + protected: virtual int recv_dev(uint8_t **buf) override; + private: pcap_t *m_p; #ifdef SDLMAME_MACOSX @@ -124,7 +135,8 @@ private: }; #ifdef SDLMAME_MACOSX -static void netdev_pcap_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { +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; if(!ctx->p) return; @@ -149,8 +161,8 @@ static void *netdev_pcap_blocker(void *arg) { } #endif -netdev_pcap::netdev_pcap(const char *name, class device_network_interface *ifdev, int rate) - : osd_netdev(ifdev, rate) +netdev_pcap::netdev_pcap(const char *name, network_handler &ifdev) + : osd_network_device(ifdev) { char errbuf[PCAP_ERRBUF_SIZE]; #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) @@ -170,7 +182,7 @@ netdev_pcap::netdev_pcap(const char *name, class device_network_interface *ifdev m_p = nullptr; return; } - netdev_pcap::set_mac(get_mac()); + netdev_pcap::set_mac(&get_mac()[0]); #ifdef SDLMAME_MACOSX m_ctx.head = 0; @@ -180,17 +192,16 @@ netdev_pcap::netdev_pcap(const char *name, class device_network_interface *ifdev #endif } -void netdev_pcap::set_mac(const char *mac) +void netdev_pcap::set_mac(const uint8_t *mac) { - char filter[256]; struct bpf_program fp; if(!m_p) return; #ifdef SDLMAME_MACOSX - sprintf(filter, "not ether src %.2X:%.2X:%.2X:%.2X:%.2X:%.2X and (ether dst %.2X:%.2X:%.2X:%.2X:%.2X:%.2X 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]); + 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 - sprintf(filter, "ether dst %.2X:%.2X:%.2X:%.2X:%.2X:%.2X 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]); + 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, 1, 0) == -1) { + 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) { @@ -250,11 +261,11 @@ netdev_pcap::~netdev_pcap() static CREATE_NETDEV(create_pcap) { - auto *dev = new netdev_pcap(ifname, ifdev, rate); - return dynamic_cast<osd_netdev *>(dev); + auto *dev = new netdev_pcap(ifname, ifdev); + return dynamic_cast<osd_network_device *>(dev); } -int pcap_module::init(const osd_options &options) +int pcap_module::init(osd_interface &osd, const osd_options &options) { pcap_if_t *devs; char errbuf[PCAP_ERRBUF_SIZE]; @@ -285,12 +296,15 @@ void pcap_module::exit() clear_netdev(); } +} // anonymous namespace + +} // namespace osd + #else - #include "modules/osdmodule.h" - #include "netdev_module.h" - MODULE_NOT_SUPPORTED(pcap_module, OSD_NETDEV_PROVIDER, "pcap") +namespace osd { namespace { MODULE_NOT_SUPPORTED(pcap_module, OSD_NETDEV_PROVIDER, "pcap") } } + #endif -MODULE_DEFINITION(NETDEV_PCAP, pcap_module) +MODULE_DEFINITION(NETDEV_PCAP, osd::pcap_module) diff --git a/src/osd/modules/netdev/taptun.cpp b/src/osd/modules/netdev/taptun.cpp index c0d99ec750c..a2953f33efc 100644 --- a/src/osd/modules/netdev/taptun.cpp +++ b/src/osd/modules/netdev/taptun.cpp @@ -1,5 +1,9 @@ // license:BSD-3-Clause // copyright-holders:Carl +#include "netdev_module.h" + +#include "modules/osdmodule.h" + #if defined(OSD_NET_USE_TAPTUN) #if defined(_WIN32) @@ -13,12 +17,13 @@ #include <cerrno> #endif -#include "emu.h" +#include "osdcore.h" // osd_printf_verbose +#include "osdfile.h" // PATH_SEPARATOR #include "osdnet.h" -#include "modules/osdmodule.h" -#include "netdev_module.h" #include "unicode.h" +#include "util/hashing.h" // crc32_creator + #ifdef __linux__ #define IFF_TAP 0x0002 #define IFF_NO_PI 0x1000 @@ -31,6 +36,10 @@ #define PRODUCT_TAP_WIN_COMPONENT_ID "tap0901" #endif +namespace osd { + +namespace { + // Ethernet minimum frame length static constexpr int ETHERNET_MIN_FRAME = 64; @@ -43,7 +52,7 @@ public: } virtual ~taptun_module() { } - virtual int init(const osd_options &options); + virtual int init(osd_interface &osd, const osd_options &options); virtual void exit(); virtual bool probe() { return true; } @@ -51,16 +60,18 @@ public: -class netdev_tap : public osd_netdev +class netdev_tap : public osd_network_device { public: - netdev_tap(const char *name, class device_network_interface *ifdev, int rate); + netdev_tap(const char *name, network_handler &ifdev); ~netdev_tap(); int send(uint8_t *buf, int len) override; - void set_mac(const char *mac) override; + void set_mac(const uint8_t *mac) override; + protected: int recv_dev(uint8_t **buf) override; + private: #if defined(_WIN32) HANDLE m_handle = INVALID_HANDLE_VALUE; @@ -74,8 +85,8 @@ private: 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 &ifdev) + : osd_network_device(ifdev) { #ifdef __linux__ struct ifreq ifr; @@ -134,7 +145,7 @@ netdev_tap::~netdev_tap() #endif } -void netdev_tap::set_mac(const char *mac) +void netdev_tap::set_mac(const uint8_t *mac) { memcpy(m_mac, mac, 6); } @@ -286,16 +297,21 @@ static std::vector<std::wstring> get_tap_adapters() // check if the ComponentId value indicates a TAP-Windows adapter if (RegQueryValueExW(unit_key, L"ComponentId", NULL, &data_type, LPBYTE(component_id), &component_id_len) == ERROR_SUCCESS - && data_type == REG_SZ - && safe_string(component_id, component_id_len) == L"" PRODUCT_TAP_WIN_COMPONENT_ID) + && data_type == REG_SZ) { - WCHAR net_cfg_instance_id[MAX_PATH]; - DWORD net_cfg_instance_id_len = sizeof(net_cfg_instance_id); - - // add the adapter to the result - if (RegQueryValueExW(unit_key, L"NetCfgInstanceId", NULL, &data_type, LPBYTE(net_cfg_instance_id), &net_cfg_instance_id_len) == ERROR_SUCCESS - && data_type == REG_SZ) - result.push_back(safe_string(net_cfg_instance_id, net_cfg_instance_id_len)); + std::wstring const value(safe_string(component_id, component_id_len)); + + // some older versions may not set the "root\" prefix + if (value == L"root\\" PRODUCT_TAP_WIN_COMPONENT_ID || value == L"" PRODUCT_TAP_WIN_COMPONENT_ID) + { + WCHAR net_cfg_instance_id[MAX_PATH]; + DWORD net_cfg_instance_id_len = sizeof(net_cfg_instance_id); + + // add the adapter to the result + if (RegQueryValueExW(unit_key, L"NetCfgInstanceId", NULL, &data_type, LPBYTE(net_cfg_instance_id), &net_cfg_instance_id_len) == ERROR_SUCCESS + && data_type == REG_SZ) + result.push_back(safe_string(net_cfg_instance_id, net_cfg_instance_id_len)); + } } RegCloseKey(unit_key); @@ -326,7 +342,7 @@ int netdev_tap::recv_dev(uint8_t **buf) // 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)); + } while((len > 0) && memcmp(&get_mac()[0], m_buf, 6) && !get_promisc() && !(m_buf[0] & 1)); if (len > 0) len = finalise_frame(m_buf, len); @@ -338,11 +354,11 @@ int netdev_tap::recv_dev(uint8_t **buf) static CREATE_NETDEV(create_tap) { - auto *dev = new netdev_tap(ifname, ifdev, rate); - return dynamic_cast<osd_netdev *>(dev); + auto *dev = new netdev_tap(ifname, ifdev); + return dynamic_cast<osd_network_device *>(dev); } -int taptun_module::init(const osd_options &options) +int taptun_module::init(osd_interface &osd, const osd_options &options) { #if defined(_WIN32) for (std::wstring &id : get_tap_adapters()) @@ -358,13 +374,16 @@ void taptun_module::exit() clear_netdev(); } +} // anonymous namespace + +} // namespace osd + #else - #include "modules/osdmodule.h" - #include "netdev_module.h" - MODULE_NOT_SUPPORTED(taptun_module, OSD_NETDEV_PROVIDER, "taptun") +namespace osd { namespace { MODULE_NOT_SUPPORTED(taptun_module, OSD_NETDEV_PROVIDER, "taptun") } } + #endif -MODULE_DEFINITION(NETDEV_TAPTUN, taptun_module) +MODULE_DEFINITION(NETDEV_TAPTUN, osd::taptun_module) diff --git a/src/osd/modules/opengl/gl_shader_mgr.cpp b/src/osd/modules/opengl/gl_shader_mgr.cpp index ea09a7fa6a3..4567a8e8085 100644 --- a/src/osd/modules/opengl/gl_shader_mgr.cpp +++ b/src/osd/modules/opengl/gl_shader_mgr.cpp @@ -1,13 +1,17 @@ // license:BSD-3-Clause // copyright-holders:Sven Gothel -#include <cstdio> /* snprintf */ -#include <cstdlib> /* malloc */ + +#include "gl_shader_mgr.h" #include "osdcomm.h" -#include "osd_opengl.h" -#include "gl_shader_mgr.h" -#include "gl_shader_tool.h" +#include "util/strformat.h" + +#include <algorithm> +#include <iterator> + + +namespace { #define GLSL_VERTEX_SHADER_INT_NUMBER 1 // general #define GLSL_VERTEX_SHADER_MAX_NUMBER 2 // general + custom @@ -15,16 +19,16 @@ #ifdef GLSL_SOURCE_ON_DISK -static char const *const glsl_mamebm_vsh_files [GLSL_VERTEX_SHADER_INT_NUMBER] = +static char const *const glsl_mamebm_vsh_files[GLSL_VERTEX_SHADER_INT_NUMBER] = { - "/tmp/glsl_general.vsh" // general + "/tmp/glsl_general.vsh" // general }; -static char const *const glsl_mamebm_fsh_files [GLSL_SHADER_FEAT_INT_NUMBER] = +static char const *const glsl_mamebm_fsh_files[glsl_shader_info::FEAT_INT_NUMBER] = { - "/tmp/glsl_plain_rgb32_dir.fsh", // rgb32 dir plain - "/tmp/glsl_bilinear_rgb32_dir.fsh", // rgb32 dir bilinear - "/tmp/glsl_bicubic_rgb32_dir.fsh", // rgb32 dir bicubic + "/tmp/glsl_plain_rgb32_dir.fsh", // rgb32 dir plain + "/tmp/glsl_bilinear_rgb32_dir.fsh", // rgb32 dir bilinear + "/tmp/glsl_bicubic_rgb32_dir.fsh", // rgb32 dir bicubic }; #else // GLSL_SOURCE_ON_DISK @@ -35,207 +39,239 @@ static char const *const glsl_mamebm_fsh_files [GLSL_SHADER_FEAT_INT_NUMBER] = #include "shader/glsl_bilinear_rgb32_dir.fsh.c" #include "shader/glsl_bicubic_rgb32_dir.fsh.c" -static char const *const glsl_mamebm_vsh_sources [GLSL_VERTEX_SHADER_INT_NUMBER] = +static char const *const glsl_mamebm_vsh_sources[GLSL_VERTEX_SHADER_INT_NUMBER] = { - glsl_general_vsh_src // general + glsl_general_vsh_src // general }; -static char const *const glsl_mamebm_fsh_sources [GLSL_SHADER_FEAT_INT_NUMBER] = +static char const *const glsl_mamebm_fsh_sources[glsl_shader_info::FEAT_INT_NUMBER] = { - glsl_plain_rgb32_dir_fsh_src, // rgb32 dir plain - glsl_bilinear_rgb32_dir_fsh_src, // rgb32 dir bilinear - glsl_bicubic_rgb32_dir_fsh_src, // rgb32 dir bicubic + glsl_plain_rgb32_dir_fsh_src, // rgb32 dir plain + glsl_bilinear_rgb32_dir_fsh_src, // rgb32 dir bilinear + glsl_bicubic_rgb32_dir_fsh_src, // rgb32 dir bicubic }; #endif // GLSL_SOURCE_ON_DISK -static char const *const glsl_mamebm_filter_names [GLSL_SHADER_FEAT_MAX_NUMBER] = -{ - "plain", - "bilinear", - "bicubic", - "custom" -}; - -static GLhandleARB glsl_mamebm_programs [GLSL_SHADER_FEAT_MAX_NUMBER+9] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* rgb32 dir: plain, bilinear, bicubic, custom0-9, .. */ -}; +static char const *const glsl_mamebm_filter_names[glsl_shader_info::FEAT_MAX_NUMBER]{ + "plain", + "bilinear", + "bicubic", + "custom" }; /** * fragment shader -> vertex shader mapping */ -static int glsl_mamebm_fsh2vsh[GLSL_SHADER_FEAT_MAX_NUMBER] = +static const int glsl_mamebm_fsh2vsh[glsl_shader_info::FEAT_MAX_NUMBER] = { 0, // plain -> general 0, // bilinear -> general 0, // bicubic -> general - 1, // custom -> custom -}; - -static GLhandleARB glsl_mamebm_vsh_shader[GLSL_VERTEX_SHADER_MAX_NUMBER+9] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; /* general, custom0-9 */ - -static GLhandleARB glsl_mamebm_fsh_shader [GLSL_SHADER_FEAT_MAX_NUMBER+9] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* rgb32 dir: plain, bilinear, bicubic, custom0-9 */ + 1, // custom -> custom }; -static GLhandleARB glsl_scrn_programs [10] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* rgb32: custom0-9, .. */ -}; - -static GLhandleARB glsl_scrn_vsh_shader[10] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; /* custom0-9 */ - -static GLhandleARB glsl_scrn_fsh_shader [10] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; /* rgb32: custom0-9 */ - -const char * glsl_shader_get_filter_name_mamebm(int glslShaderFeature) -{ - if ( !(0 <= glslShaderFeature && glslShaderFeature < GLSL_SHADER_FEAT_MAX_NUMBER) ) - return "illegal shader feature"; - - return glsl_mamebm_filter_names[glslShaderFeature]; -} - -GLhandleARB glsl_shader_get_program_mamebm(int glslShaderFeature, int idx) -{ - if ( !(0 <= glslShaderFeature && glslShaderFeature < GLSL_SHADER_FEAT_MAX_NUMBER) ) - return 0; - - return glsl_mamebm_programs[glslShaderFeature+idx]; -} - -GLhandleARB glsl_shader_get_program_scrn(int idx) +class glsl_shader_impl : public glsl_shader_info { - if ( !(0 <= idx && idx < 10) ) - return 0; +public: + glsl_shader_impl( +#if defined(USE_DISPATCH_GL) + osd_gl_dispatch *gld +#endif + ) +#if defined(USE_DISPATCH_GL) + : glsl_shader_info(gld) +#endif + { + std::fill(std::begin(mamebm_programs), std::end(mamebm_programs), GLhandleARB(0)); + std::fill(std::begin(mamebm_vsh_shader), std::end(mamebm_vsh_shader), GLhandleARB(0)); + std::fill(std::begin(mamebm_fsh_shader), std::end(mamebm_fsh_shader), GLhandleARB(0)); + std::fill(std::begin(scrn_programs), std::end(scrn_programs), GLhandleARB(0)); + std::fill(std::begin(scrn_vsh_shader), std::end(scrn_vsh_shader), GLhandleARB(0)); + std::fill(std::begin(scrn_fsh_shader), std::end(scrn_fsh_shader), GLhandleARB(0)); + } - return glsl_scrn_programs[idx]; -} + bool initalize(osd_gl_context &gl_ctx) + { + if (!load_extension(gl_ctx)) + return false; -glsl_shader_info *glsl_shader_init(osd_gl_context *gl_ctx) -{ - int i,j, err; + int err = 0; - err = gl_shader_loadExtention(gl_ctx); - if(err) return nullptr; + for (int i = 0; !err && (i < GLSL_VERTEX_SHADER_INT_NUMBER); i++) + { +#ifdef GLSL_SOURCE_ON_DISK + if (glsl_mamebm_vsh_files[i]) + err = compile_file( + &mamebm_vsh_shader[i], + GL_VERTEX_SHADER_ARB, + glsl_mamebm_vsh_files[i], + false); +#else + if (glsl_mamebm_vsh_sources[i]) + err = compile_source( + &mamebm_vsh_shader[i], + GL_VERTEX_SHADER_ARB, + glsl_mamebm_vsh_sources[i], + false); +#endif + } + if (err) + return false; + + for (int j = 0; !err && (j < FEAT_INT_NUMBER); j++) + { +#ifdef GLSL_SOURCE_ON_DISK + if (glsl_mamebm_fsh_files[j]) + err = compile_files( + &mamebm_programs[j], + &mamebm_vsh_shader[glsl_mamebm_fsh2vsh[j]], + &mamebm_fsh_shader[j], + nullptr, // precompiled + glsl_mamebm_fsh_files[j], + false); +#else + if (glsl_mamebm_fsh_sources[j]) + err = compile_sources( + &mamebm_programs[j], + &mamebm_vsh_shader[glsl_mamebm_fsh2vsh[j]], + &mamebm_fsh_shader[j], + nullptr, // precompiled + glsl_mamebm_fsh_sources[j]); +#endif + } + if (err) + return false; + + return true; + } - for (i=0; !err && i<GLSL_VERTEX_SHADER_INT_NUMBER; i++) + virtual ~glsl_shader_impl() { - #ifdef GLSL_SOURCE_ON_DISK - if(glsl_mamebm_vsh_files[i]) - err = gl_compile_shader_file ( &glsl_mamebm_vsh_shader[i], GL_VERTEX_SHADER_ARB, - glsl_mamebm_vsh_files[i], 0); - #else - if(glsl_mamebm_vsh_sources[i]) - err = gl_compile_shader_source( &glsl_mamebm_vsh_shader[i], GL_VERTEX_SHADER_ARB, - glsl_mamebm_vsh_sources[i], 0); - #endif - } + pfn_glUseProgramObjectARB(0); + glFinish(); - if(err) return nullptr; + for (int i = 0; i < GLSL_VERTEX_SHADER_MAX_NUMBER + 9; i++) + { + if (mamebm_vsh_shader[i]) + delete_shader(nullptr, &mamebm_vsh_shader[i], nullptr); + } + + for (int j = 0; j < FEAT_MAX_NUMBER + 9; j++) + { + if (mamebm_fsh_shader[j]) + delete_shader(nullptr, nullptr, &mamebm_fsh_shader[j]); + } + + for (int j = 0; j < FEAT_MAX_NUMBER + 9; j++) + { + if (mamebm_programs[j]) + delete_shader(&mamebm_programs[j], nullptr, nullptr); + } + + for (int i = 0; i < 10; i++) + { + if (scrn_vsh_shader[i]) + delete_shader(nullptr, &scrn_vsh_shader[i], nullptr); + if (scrn_fsh_shader[i]) + delete_shader(nullptr, nullptr, &scrn_fsh_shader[i]); + if (scrn_programs[i]) + delete_shader(&scrn_programs[i], nullptr, nullptr); + } + } - for (j=0; !err && j<GLSL_SHADER_FEAT_INT_NUMBER; j++) + virtual const char *get_filter_name_mamebm(int glslShaderFeature) override { - #ifdef GLSL_SOURCE_ON_DISK - if(glsl_mamebm_fsh_files[j]) - err = gl_compile_shader_files (&glsl_mamebm_programs[j], - &glsl_mamebm_vsh_shader[glsl_mamebm_fsh2vsh[j]], - &glsl_mamebm_fsh_shader[j], - nullptr /*precompiled*/, glsl_mamebm_fsh_files[j], 0); - #else - if(glsl_mamebm_fsh_sources[j]) - err = gl_compile_shader_sources(&glsl_mamebm_programs[j], - &glsl_mamebm_vsh_shader[glsl_mamebm_fsh2vsh[j]], - &glsl_mamebm_fsh_shader[j], - nullptr /*precompiled*/, glsl_mamebm_fsh_sources[j]); - #endif - } - if (err) return nullptr; - return new glsl_shader_info{ 0 }; -} + if ((0 > glslShaderFeature) || (glslShaderFeature >= FEAT_MAX_NUMBER)) + return "illegal shader feature"; -int glsl_shader_free(glsl_shader_info *shinfo) -{ - pfn_glUseProgramObjectARB(0); // back to fixed function pipeline - glFinish(); + return glsl_mamebm_filter_names[glslShaderFeature]; + } - for (int i=0; i<GLSL_VERTEX_SHADER_MAX_NUMBER+9; i++) + virtual GLhandleARB get_program_mamebm(int glslShaderFeature, int idx) override { - if ( glsl_mamebm_vsh_shader[i] ) - (void) gl_delete_shader(nullptr, &glsl_mamebm_vsh_shader[i], nullptr); + if ((0 > glslShaderFeature) || (glslShaderFeature >= FEAT_MAX_NUMBER)) + return 0; + + return mamebm_programs[glslShaderFeature + idx]; } - for (int j=0; j<GLSL_SHADER_FEAT_MAX_NUMBER+9; j++) + virtual GLhandleARB get_program_scrn(int idx) override { - if ( glsl_mamebm_fsh_shader[j] ) - (void) gl_delete_shader(nullptr, nullptr, &glsl_mamebm_fsh_shader[j]); + if ((0 > idx) || (idx >= 10)) + return 0; + + return scrn_programs[idx]; } - for (int j=0; j<GLSL_SHADER_FEAT_MAX_NUMBER+9; j++) + virtual int add_mamebm(const char *custShaderPrefix, int idx) override { - if ( glsl_mamebm_programs[j] ) - (void) gl_delete_shader( &glsl_mamebm_programs[j], nullptr, nullptr); + int err; + + err = compile_file( + &mamebm_vsh_shader[GLSL_VERTEX_SHADER_CUSTOM + idx], + GL_VERTEX_SHADER_ARB, + util::string_format("%s.vsh", custShaderPrefix).c_str(), + false); + if (err) + return err; + + err = compile_files( + &mamebm_programs[FEAT_CUSTOM + idx], + &mamebm_vsh_shader[GLSL_VERTEX_SHADER_CUSTOM + idx], + &mamebm_fsh_shader[FEAT_CUSTOM + idx], + nullptr, // precompiled + util::string_format("%s_rgb32_dir.fsh", custShaderPrefix).c_str(), + false); + return err; } - for (int i=0; i<10; i++) + virtual int add_scrn(const char *custShaderPrefix, int idx) override { - if ( glsl_scrn_vsh_shader[i] ) - (void) gl_delete_shader(nullptr, &glsl_scrn_vsh_shader[i], nullptr); - if ( glsl_scrn_fsh_shader[i] ) - (void) gl_delete_shader(nullptr, nullptr, &glsl_scrn_fsh_shader[i]); - if ( glsl_scrn_programs[i] ) - (void) gl_delete_shader( &glsl_scrn_programs[i], nullptr, nullptr); + int err; + + err = compile_file( + &scrn_vsh_shader[idx], + GL_VERTEX_SHADER_ARB, + util::string_format("%s.vsh", custShaderPrefix).c_str(), + false); + if (err) + return err; + + err = compile_files( + &scrn_programs[idx], + &scrn_vsh_shader[idx], + &scrn_fsh_shader[idx], + nullptr, // precompiled + util::string_format("%s.fsh", custShaderPrefix).c_str(), + false); + return err; } - delete shinfo; - return 0; -} - -int glsl_shader_add_mamebm(glsl_shader_info *shinfo, const char * custShaderPrefix, int idx) -{ - int err; - static char fname[8192]; - - snprintf(fname, 8192, "%s.vsh", custShaderPrefix); fname[8191]=0; - - err = gl_compile_shader_file ( &glsl_mamebm_vsh_shader[GLSL_VERTEX_SHADER_CUSTOM+idx], GL_VERTEX_SHADER_ARB, fname, 0); - if(err) return err; - - snprintf(fname, 8192, "%s_rgb32_dir.fsh", custShaderPrefix); fname[8191]=0; +private: + GLhandleARB mamebm_programs[FEAT_MAX_NUMBER + 9]; // rgb32 dir: plain, bilinear, bicubic, custom0-9, .. + GLhandleARB mamebm_vsh_shader[GLSL_VERTEX_SHADER_MAX_NUMBER + 9]; // general, custom0-9 + GLhandleARB mamebm_fsh_shader[FEAT_MAX_NUMBER+9]; // rgb32 dir: plain, bilinear, bicubic, custom0-9 + GLhandleARB scrn_programs[10]; // rgb32: custom0-9, .. + GLhandleARB scrn_vsh_shader[10]; // custom0-9 + GLhandleARB scrn_fsh_shader[10]; // rgb32: custom0-9 +}; - err = gl_compile_shader_files (&glsl_mamebm_programs[GLSL_SHADER_FEAT_CUSTOM+idx], - &glsl_mamebm_vsh_shader[GLSL_VERTEX_SHADER_CUSTOM+idx], - &glsl_mamebm_fsh_shader[GLSL_SHADER_FEAT_CUSTOM+idx], - nullptr /*precompiled*/, fname, 0); +} // anonymous namespace - return err; -} -int glsl_shader_add_scrn(glsl_shader_info *shinfo, const char * custShaderPrefix, int idx) +std::unique_ptr<glsl_shader_info> glsl_shader_info::init( + osd_gl_context &gl_ctx +#if defined(USE_DISPATCH_GL) + , osd_gl_dispatch *gld +#endif + ) { - int err; - static char fname[8192]; - - snprintf(fname, 8192, "%s.vsh", custShaderPrefix); fname[8191]=0; - - err = gl_compile_shader_file ( &glsl_scrn_vsh_shader[idx], GL_VERTEX_SHADER_ARB, fname, 0); - if(err) return err; - - snprintf(fname, 8192, "%s.fsh", custShaderPrefix); fname[8191]=0; - - err = gl_compile_shader_files (&glsl_scrn_programs[idx], - &glsl_scrn_vsh_shader[idx], - &glsl_scrn_fsh_shader[idx], - nullptr /*precompiled*/, fname, 0); - return err; + auto result = std::make_unique<glsl_shader_impl>( +#if defined(USE_DISPATCH_GL) + gld +#endif + ); + if (!result->initalize(gl_ctx)) + result.reset(); + return result; } diff --git a/src/osd/modules/opengl/gl_shader_mgr.h b/src/osd/modules/opengl/gl_shader_mgr.h index f95ec8c2244..ad9e248ac5f 100644 --- a/src/osd/modules/opengl/gl_shader_mgr.h +++ b/src/osd/modules/opengl/gl_shader_mgr.h @@ -1,51 +1,57 @@ // license:BSD-3-Clause // copyright-holders:Sven Gothel +#ifndef MAME_OSD_OPENGL_GL_SHADER_MGR_H +#define MAME_OSD_OPENGL_GL_SHADER_MGR_H -#ifndef GL_SHADER_MGR_H -#define GL_SHADER_MGR_H +#pragma once +#include "gl_shader_tool.h" #include "osd_opengl.h" +#include <memory> + + // #define GLSL_SOURCE_ON_DISK 1 -enum GLSL_SHADER_FEATURE { - GLSL_SHADER_FEAT_PLAIN, - GLSL_SHADER_FEAT_BILINEAR, - GLSL_SHADER_FEAT_BICUBIC, - GLSL_SHADER_FEAT_INT_NUMBER, - GLSL_SHADER_FEAT_CUSTOM = GLSL_SHADER_FEAT_INT_NUMBER, - GLSL_SHADER_FEAT_MAX_NUMBER -}; -// old code passed sdl_info * to functions here -// however the parameter was not used -// changed interface to more generic one. -struct glsl_shader_info +class glsl_shader_info : public gl_shader_tool { - int dummy; // avoid compiler breakage +public: + enum FEATURE + { + FEAT_PLAIN, + FEAT_BILINEAR, + FEAT_BICUBIC, + FEAT_INT_NUMBER, + FEAT_CUSTOM = FEAT_INT_NUMBER, + FEAT_MAX_NUMBER + }; + + using gl_shader_tool::gl_shader_tool; + + virtual ~glsl_shader_info() = default; + + /** + * returns the GLSL program if ok/available, otherwise 0 + */ + virtual GLhandleARB get_program_mamebm(int glslShaderFeature, int idx) = 0; + + virtual const char *get_filter_name_mamebm(int glslShaderFeature) = 0; + + virtual int add_mamebm(const char *custShaderPrefix, int idx) = 0; + + virtual GLhandleARB get_program_scrn(int idx) = 0; + virtual int add_scrn(const char *custShaderPrefix, int idx) = 0; + + /** + * returns pointer if OK, otherwise nullptr + */ + static std::unique_ptr<glsl_shader_info> init( + osd_gl_context &gl_ctx +#if defined(USE_DISPATCH_GL) + , osd_gl_dispatch *gld +#endif + ); }; - -/** - * returns pointer if ok, otherwise nullptr - */ -glsl_shader_info *glsl_shader_init(osd_gl_context *gl_ctx); - -/** - * returns 0 if ok, otherwise an error value - */ -int glsl_shader_free(glsl_shader_info *shinfo); - -/** - * returns the GLSL program if ok/available, otherwise 0 - */ -GLhandleARB glsl_shader_get_program_mamebm(int glslShaderFeature, int idx); - -const char * glsl_shader_get_filter_name_mamebm(int glslShaderFeature); - -int glsl_shader_add_mamebm(glsl_shader_info *shinfo, const char * custShaderPrefix, int idx); - -GLhandleARB glsl_shader_get_program_scrn(int idx); -int glsl_shader_add_scrn(glsl_shader_info *shinfo, const char * custShaderPrefix, int idx); - -#endif // GL_SHADER_MGR_H +#endif // MAME_OSD_OPENGL_GL_SHADER_MGR_H diff --git a/src/osd/modules/opengl/gl_shader_tool.cpp b/src/osd/modules/opengl/gl_shader_tool.cpp index 5696d14f834..fb880178b98 100644 --- a/src/osd/modules/opengl/gl_shader_tool.cpp +++ b/src/osd/modules/opengl/gl_shader_tool.cpp @@ -15,59 +15,90 @@ * */ -#include "emu.h" #include "gl_shader_tool.h" -PFNGLGETOBJECTPARAMETERIVARBPROC pfn_glGetObjectParameterivARB= nullptr; -PFNGLGETINFOLOGARBPROC pfn_glGetInfoLogARB= nullptr; -PFNGLDELETEOBJECTARBPROC pfn_glDeleteObjectARB= nullptr; -PFNGLCREATESHADEROBJECTARBPROC pfn_glCreateShaderObjectARB= nullptr; -PFNGLSHADERSOURCEARBPROC pfn_glShaderSourceARB= nullptr; -PFNGLCOMPILESHADERARBPROC pfn_glCompileShaderARB= nullptr; -PFNGLCREATEPROGRAMOBJECTARBPROC pfn_glCreateProgramObjectARB= nullptr; -PFNGLATTACHOBJECTARBPROC pfn_glAttachObjectARB= nullptr; -PFNGLLINKPROGRAMARBPROC pfn_glLinkProgramARB= nullptr; -PFNGLVALIDATEPROGRAMARBPROC pfn_glValidateProgramARB= nullptr; -PFNGLUSEPROGRAMOBJECTARBPROC pfn_glUseProgramObjectARB= nullptr; -PFNGLGETUNIFORMLOCATIONARBPROC pfn_glGetUniformLocationARB= nullptr; -PFNGLUNIFORM1FARBPROC pfn_glUniform1fARB= nullptr; -PFNGLUNIFORM1IARBPROC pfn_glUniform1iARB= nullptr; -PFNGLUNIFORM1FVARBPROC pfn_glUniform1fvARB= nullptr; -PFNGLUNIFORM2FVARBPROC pfn_glUniform2fvARB= nullptr; -PFNGLUNIFORM3FVARBPROC pfn_glUniform3fvARB= nullptr; -PFNGLUNIFORM4FVARBPROC pfn_glUniform4fvARB= nullptr; -PFNGLUNIFORM1IVARBPROC pfn_glUniform1ivARB= nullptr; -PFNGLUNIFORM2IVARBPROC pfn_glUniform2ivARB= nullptr; -PFNGLUNIFORM3IVARBPROC pfn_glUniform3ivARB= nullptr; -PFNGLUNIFORM4IVARBPROC pfn_glUniform4ivARB= nullptr; - - -int gl_shader_loadExtention(osd_gl_context *gl_ctx) +#include "osdcore.h" + + +#define GL_CHECK_ERROR_VERBOSE() check_error(CHECK_ALWAYS_VERBOSE, __FILE__, __LINE__) +#define GL_CHECK_ERROR_NORMAL() check_error(CHECK_VERBOSE, __FILE__, __LINE__) +#define GL_CHECK_ERROR_QUIET() check_error(CHECK_QUIET, __FILE__, __LINE__) +#define GL_CHECK_ERROR(m) check_error(m, __FILE__, __LINE__) + +#define GL_SHADER_CHECK_ERROR_VERBOSE(o,q) check_error(o, q, CHECK_ALWAYS_VERBOSE, __FILE__, __LINE__) +#define GL_SHADER_CHECK_ERROR_NORMAL(o,q) check_error(o, q, CHECK_VERBOSE, __FILE__, __LINE__) +#define GL_SHADER_CHECK_ERROR_QUIET(o,q) check_error(o, q, CHECK_QUIET, __FILE__, __LINE__) +#define GL_SHADER_CHECK_ERROR(o,q,m) check_error(o, q, m, __FILE__, __LINE__) + +#ifdef GL_SHADER_TOOL_DEBUG + #define GL_SHADER_CHECK(o, q) GL_SHADER_CHECK_ERROR_VERBOSE(o, q) + #define GL_CHECK() GL_CHECK_ERROR_VERBOSE() +#else + #define GL_SHADER_CHECK(o, q) GL_SHADER_CHECK_ERROR_NORMAL(o, q) + #define GL_CHECK() GL_CHECK_ERROR_NORMAL() +#endif + + +gl_shader_tool::gl_shader_tool( +#if defined(USE_DISPATCH_GL) + osd_gl_dispatch *gld +#endif + ) + : pfn_glGetObjectParameterivARB(nullptr) + , pfn_glGetInfoLogARB(nullptr) + , pfn_glDeleteObjectARB(nullptr) + , pfn_glCreateShaderObjectARB(nullptr) + , pfn_glShaderSourceARB(nullptr) + , pfn_glCompileShaderARB(nullptr) + , pfn_glCreateProgramObjectARB(nullptr) + , pfn_glAttachObjectARB(nullptr) + , pfn_glLinkProgramARB(nullptr) + , pfn_glValidateProgramARB(nullptr) + , pfn_glUseProgramObjectARB(nullptr) + , pfn_glGetUniformLocationARB(nullptr) + , pfn_glUniform1fARB(nullptr) + , pfn_glUniform1iARB(nullptr) + , pfn_glUniform1fvARB(nullptr) + , pfn_glUniform2fvARB(nullptr) + , pfn_glUniform3fvARB(nullptr) + , pfn_glUniform4fvARB(nullptr) + , pfn_glUniform1ivARB(nullptr) + , pfn_glUniform2ivARB(nullptr) + , pfn_glUniform3ivARB(nullptr) + , pfn_glUniform4ivARB(nullptr) +#if defined(USE_DISPATCH_GL) + , gl_dispatch(gld) +#endif +{ +} + + +bool gl_shader_tool::load_extension(osd_gl_context &gl_ctx) { - pfn_glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) gl_ctx->getProcAddress("glGetObjectParameterivARB"); - pfn_glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) gl_ctx->getProcAddress ("glGetInfoLogARB"); - pfn_glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) gl_ctx->getProcAddress ("glDeleteObjectARB"); - pfn_glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) gl_ctx->getProcAddress ("glCreateShaderObjectARB"); - pfn_glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) gl_ctx->getProcAddress ("glShaderSourceARB"); - pfn_glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) gl_ctx->getProcAddress ("glCompileShaderARB"); - pfn_glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) gl_ctx->getProcAddress ("glCreateProgramObjectARB"); - pfn_glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) gl_ctx->getProcAddress ("glAttachObjectARB"); - pfn_glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) gl_ctx->getProcAddress ("glLinkProgramARB"); - pfn_glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) gl_ctx->getProcAddress ("glValidateProgramARB"); - pfn_glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) gl_ctx->getProcAddress ("glUseProgramObjectARB"); - pfn_glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) gl_ctx->getProcAddress ("glGetUniformLocationARB"); - pfn_glUniform1fARB = (PFNGLUNIFORM1FARBPROC) gl_ctx->getProcAddress ("glUniform1fARB"); - pfn_glUniform1iARB = (PFNGLUNIFORM1IARBPROC) gl_ctx->getProcAddress ("glUniform1iARB"); - pfn_glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) gl_ctx->getProcAddress ("glUniform1fvARB"); - pfn_glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) gl_ctx->getProcAddress ("glUniform2fvARB"); - pfn_glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) gl_ctx->getProcAddress ("glUniform3fvARB"); - pfn_glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) gl_ctx->getProcAddress ("glUniform4fvARB"); - pfn_glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) gl_ctx->getProcAddress ("glUniform1ivARB"); - pfn_glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) gl_ctx->getProcAddress ("glUniform2ivARB"); - pfn_glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) gl_ctx->getProcAddress ("glUniform3ivARB"); - pfn_glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) gl_ctx->getProcAddress ("glUniform4ivARB"); - - if ( pfn_glGetObjectParameterivARB && pfn_glGetInfoLogARB && pfn_glDeleteObjectARB && pfn_glCreateShaderObjectARB && + gl_ctx.get_proc_address(pfn_glGetObjectParameterivARB, "glGetObjectParameterivARB"); + gl_ctx.get_proc_address(pfn_glGetInfoLogARB, "glGetInfoLogARB"); + gl_ctx.get_proc_address(pfn_glDeleteObjectARB, "glDeleteObjectARB"); + gl_ctx.get_proc_address(pfn_glCreateShaderObjectARB, "glCreateShaderObjectARB"); + gl_ctx.get_proc_address(pfn_glShaderSourceARB, "glShaderSourceARB"); + gl_ctx.get_proc_address(pfn_glCompileShaderARB, "glCompileShaderARB"); + gl_ctx.get_proc_address(pfn_glCreateProgramObjectARB, "glCreateProgramObjectARB"); + gl_ctx.get_proc_address(pfn_glAttachObjectARB, "glAttachObjectARB"); + gl_ctx.get_proc_address(pfn_glLinkProgramARB, "glLinkProgramARB"); + gl_ctx.get_proc_address(pfn_glValidateProgramARB, "glValidateProgramARB"); + gl_ctx.get_proc_address(pfn_glUseProgramObjectARB, "glUseProgramObjectARB"); + gl_ctx.get_proc_address(pfn_glGetUniformLocationARB, "glGetUniformLocationARB"); + gl_ctx.get_proc_address(pfn_glUniform1fARB, "glUniform1fARB"); + gl_ctx.get_proc_address(pfn_glUniform1iARB, "glUniform1iARB"); + gl_ctx.get_proc_address(pfn_glUniform1fvARB, "glUniform1fvARB"); + gl_ctx.get_proc_address(pfn_glUniform2fvARB, "glUniform2fvARB"); + gl_ctx.get_proc_address(pfn_glUniform3fvARB, "glUniform3fvARB"); + gl_ctx.get_proc_address(pfn_glUniform4fvARB, "glUniform4fvARB"); + gl_ctx.get_proc_address(pfn_glUniform1ivARB, "glUniform1ivARB"); + gl_ctx.get_proc_address(pfn_glUniform2ivARB, "glUniform2ivARB"); + gl_ctx.get_proc_address(pfn_glUniform3ivARB, "glUniform3ivARB"); + gl_ctx.get_proc_address(pfn_glUniform4ivARB, "glUniform4ivARB"); + + if (pfn_glGetObjectParameterivARB && pfn_glGetInfoLogARB && pfn_glDeleteObjectARB && pfn_glCreateShaderObjectARB && pfn_glShaderSourceARB && pfn_glCompileShaderARB && pfn_glCreateProgramObjectARB && pfn_glAttachObjectARB && pfn_glLinkProgramARB && pfn_glValidateProgramARB && pfn_glUseProgramObjectARB && pfn_glGetUniformLocationARB && pfn_glUniform1fARB && pfn_glUniform1iARB && @@ -75,62 +106,52 @@ int gl_shader_loadExtention(osd_gl_context *gl_ctx) pfn_glUniform1ivARB && pfn_glUniform2ivARB && pfn_glUniform3ivARB && pfn_glUniform4ivARB ) { - return 0; + return true; } osd_printf_error("OpenGL: missing ARB shader function: "); - if (!pfn_glGetObjectParameterivARB) osd_printf_error("glGetObjectParameterivARB, "); - if (!pfn_glGetInfoLogARB) osd_printf_error("glGetInfoLogARB, "); - if (!pfn_glDeleteObjectARB) osd_printf_error("glDeleteObjectARB, "); - if (!pfn_glCreateShaderObjectARB) osd_printf_error("glCreateShaderObjectARB, "); - if (!pfn_glShaderSourceARB) osd_printf_error("glShaderSourceARB, "); - if (!pfn_glCompileShaderARB) osd_printf_error("glCompileShaderARB, "); - if (!pfn_glCreateProgramObjectARB) osd_printf_error("glCreateProgramObjectARB, "); - if (!pfn_glAttachObjectARB) osd_printf_error("glAttachObjectARB, "); - if (!pfn_glLinkProgramARB) osd_printf_error("glLinkProgramARB, "); - if (!pfn_glValidateProgramARB) osd_printf_error("glValidateProgramARB, "); - if (!pfn_glUseProgramObjectARB) osd_printf_error("glUseProgramObjectARB, "); - if (!pfn_glGetUniformLocationARB) osd_printf_error("glGetUniformLocationARB, "); - if (!pfn_glUniform1fARB) osd_printf_error("glUniform1fARB, "); - if (!pfn_glUniform1iARB) osd_printf_error("glUniform1iARB"); - if (!pfn_glUniform1fvARB) osd_printf_error("glUniform1fvARB, "); - if (!pfn_glUniform2fvARB) osd_printf_error("glUniform2fvARB, "); - if (!pfn_glUniform3fvARB) osd_printf_error("glUniform3fvARB, "); - if (!pfn_glUniform4fvARB) osd_printf_error("glUniform4fvARB, "); - if (!pfn_glUniform1ivARB) osd_printf_error("glUniform1ivARB"); - if (!pfn_glUniform2ivARB) osd_printf_error("glUniform2ivARB"); - if (!pfn_glUniform3ivARB) osd_printf_error("glUniform3ivARB"); - if (!pfn_glUniform4ivARB) osd_printf_error("glUniform4ivARB"); + if (!pfn_glGetObjectParameterivARB) osd_printf_error(" glGetObjectParameterivARB"); + if (!pfn_glGetInfoLogARB) osd_printf_error(" glGetInfoLogARB"); + if (!pfn_glDeleteObjectARB) osd_printf_error(" glDeleteObjectARB"); + if (!pfn_glCreateShaderObjectARB) osd_printf_error(" glCreateShaderObjectARB"); + if (!pfn_glShaderSourceARB) osd_printf_error(" glShaderSourceARB"); + if (!pfn_glCompileShaderARB) osd_printf_error(" glCompileShaderARB"); + if (!pfn_glCreateProgramObjectARB) osd_printf_error(" glCreateProgramObjectARB"); + if (!pfn_glAttachObjectARB) osd_printf_error(" glAttachObjectARB"); + if (!pfn_glLinkProgramARB) osd_printf_error(" glLinkProgramARB"); + if (!pfn_glValidateProgramARB) osd_printf_error(" glValidateProgramARB"); + if (!pfn_glUseProgramObjectARB) osd_printf_error(" glUseProgramObjectARB"); + if (!pfn_glGetUniformLocationARB) osd_printf_error(" glGetUniformLocationARB"); + if (!pfn_glUniform1fARB) osd_printf_error(" glUniform1fARB"); + if (!pfn_glUniform1iARB) osd_printf_error(" glUniform1iARB"); + if (!pfn_glUniform1fvARB) osd_printf_error(" glUniform1fvARB"); + if (!pfn_glUniform2fvARB) osd_printf_error(" glUniform2fvARB"); + if (!pfn_glUniform3fvARB) osd_printf_error(" glUniform3fvARB"); + if (!pfn_glUniform4fvARB) osd_printf_error(" glUniform4fvARB"); + if (!pfn_glUniform1ivARB) osd_printf_error(" glUniform1ivARB"); + if (!pfn_glUniform2ivARB) osd_printf_error(" glUniform2ivARB"); + if (!pfn_glUniform3ivARB) osd_printf_error(" glUniform3ivARB"); + if (!pfn_glUniform4ivARB) osd_printf_error(" glUniform4ivARB"); osd_printf_error("\n"); - return -1; + return false; } -int gl_check_error(GLSLCheckMode m, const char *file, const int line) +int gl_shader_tool::check_error(GLSLCheckMode m, const char *file, const int line) { - GLenum glerr = glGetError(); - - if (GL_NO_ERROR != glerr) - { - if ( CHECK_VERBOSE <= m ) - { - osd_printf_warning( "%s:%d: GL Error: %d 0x%X\n", file, line, (int)glerr, (unsigned int)glerr); - } - } - return (GL_NO_ERROR != glerr)? glerr : 0; + GLenum const glerr = glGetError(); + if (GL_NO_ERROR != glerr) + { + if (CHECK_VERBOSE <= m) + osd_printf_warning("%s:%d: GL Error: %d 0x%X\n", file, line, int(glerr), unsigned(glerr)); + } + return (GL_NO_ERROR != glerr) ? glerr : 0; } -int gl_shader_check_error(GLhandleARB obj, GLenum obj_query, GLSLCheckMode m, const char *file, const int line) +int gl_shader_tool::check_error(GLhandleARB obj, GLenum obj_query, GLSLCheckMode m, const char *file, const int line) { - GLsizei length; - GLcharARB buffer[255]; - GLenum glerr; - GLint param; - int res=0; - - glerr = gl_check_error(m, file, line); - - if(!obj) + GLenum const glerr = check_error(m, file, line); + if (!obj) return glerr; if(obj_query != GL_OBJECT_TYPE_ARB && @@ -144,79 +165,88 @@ int gl_shader_check_error(GLhandleARB obj, GLenum obj_query, GLSLCheckMode m, co return -1; } + GLint param; pfn_glGetObjectParameterivARB(obj, obj_query, ¶m); + int res = 0; switch(obj_query) { - case GL_OBJECT_TYPE_ARB: - if( param!=GL_PROGRAM_OBJECT_ARB && param!=GL_SHADER_OBJECT_ARB ) + case GL_OBJECT_TYPE_ARB: + if (param != GL_PROGRAM_OBJECT_ARB && param != GL_SHADER_OBJECT_ARB) { if ( CHECK_VERBOSE <= m ) osd_printf_warning("%s:%d: GL Error: object type 0x%X generation failed\n", file, line, (unsigned int)(uintptr_t)obj); res=-1; - } else if ( CHECK_ALWAYS_VERBOSE <= m ) + } + else if (CHECK_ALWAYS_VERBOSE <= m) { - if(param==GL_PROGRAM_OBJECT_ARB) + if (param==GL_PROGRAM_OBJECT_ARB) osd_printf_warning("%s:%d: GL Error: object type 0x%X is PROGRAM, successful\n", file, line, (unsigned int)(uintptr_t)obj); else osd_printf_warning("%s:%d: GL Info: object type 0x%X is SHADER, successful\n", file, line, (unsigned int)(uintptr_t)obj); } break; - case GL_OBJECT_DELETE_STATUS_ARB: - if(param!=1) + case GL_OBJECT_DELETE_STATUS_ARB: + if (param != 1) { - if ( CHECK_ALWAYS_VERBOSE <= m ) + if (CHECK_ALWAYS_VERBOSE <= m) osd_printf_warning("%s:%d: GL Info: object 0x%X not yet marked for deletion\n", file, line, (unsigned int)(uintptr_t)obj); - } else if ( CHECK_ALWAYS_VERBOSE <= m ) + } + else if (CHECK_ALWAYS_VERBOSE <= m) { osd_printf_warning("%s:%d: GL Info: object 0x%X deletion successful\n", file, line, (unsigned int)(uintptr_t)obj); } break; - case GL_OBJECT_COMPILE_STATUS_ARB: - if(param!=1) + case GL_OBJECT_COMPILE_STATUS_ARB: + if (param != 1) { - if ( CHECK_VERBOSE <= m ) + if (CHECK_VERBOSE <= m) osd_printf_warning("%s:%d: GL Error: object 0x%X compilation failed\n", file, line, (unsigned int)(uintptr_t)obj); - res=-1; - } else if ( CHECK_ALWAYS_VERBOSE <= m ) + res = -1; + } + else if (CHECK_ALWAYS_VERBOSE <= m) { osd_printf_warning("%s:%d: GL Info: object 0x%X compiled successful\n", file, line, (unsigned int)(uintptr_t)obj); } break; - case GL_OBJECT_LINK_STATUS_ARB: - if(param!=1) + case GL_OBJECT_LINK_STATUS_ARB: + if (param != 1) { - if ( CHECK_VERBOSE <= m ) + if (CHECK_VERBOSE <= m) osd_printf_warning("%s:%d: GL Error: object 0x%X linking failed\n", file, line, (unsigned int)(uintptr_t)obj); - res=-1; - } else if ( CHECK_ALWAYS_VERBOSE <= m ) + res = -1; + } + else if (CHECK_ALWAYS_VERBOSE <= m) { osd_printf_warning("%s:%d: GL Info: object 0x%X linked successful\n", file, line, (unsigned int)(uintptr_t)obj); } break; - case GL_OBJECT_VALIDATE_STATUS_ARB: - if(param!=1) + case GL_OBJECT_VALIDATE_STATUS_ARB: + if (param != 1) { - if ( CHECK_VERBOSE <= m ) + if (CHECK_VERBOSE <= m) osd_printf_warning("%s:%d: GL Error: object 0x%X validation failed\n", file, line, (unsigned int)(uintptr_t)obj); - res=-1; - } else if ( CHECK_ALWAYS_VERBOSE <= m ) + res = -1; + } + else if (CHECK_ALWAYS_VERBOSE <= m + ) { osd_printf_warning("%s:%d: GL Info: object 0x%X validation successful\n", file, line, (unsigned int)(uintptr_t)obj); } break; - } + } - if ( res<0 || CHECK_ALWAYS_VERBOSE <= m ) + if (res < 0 || CHECK_ALWAYS_VERBOSE <= m) { + GLsizei length; + GLcharARB buffer[255]; length=0; pfn_glGetInfoLogARB(obj, sizeof(buffer), &length, buffer); - if(length>0) + if (length>0) osd_printf_warning("%s:%d glInfoLog: %s\n", file, line, buffer); } - (void) glGetError(); // ;-) - + (void)glGetError(); // ;-) return res; } @@ -234,157 +264,175 @@ int gl_round_to_pow2(int v) return v; } -int gl_texture_check_size(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, - GLint border, GLenum format, GLenum type, - GLint *avail_width, GLint *avail_height, - int verbose) +int gl_shader_tool::texture_check_size( + GLenum target, + GLint level, + GLint internalFormat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + GLint *avail_width, + GLint *avail_height, + bool verbose) { - int err=1; - GLenum texTargetProxy = (target==GL_TEXTURE_RECTANGLE_ARB)?GL_PROXY_TEXTURE_RECTANGLE_ARB:GL_PROXY_TEXTURE_2D; - - if ( !avail_width || !avail_height) - return -1; + if (!avail_width || !avail_height) + return -1; *avail_width = 0; *avail_height = 0; GL_CHECK_ERROR_QUIET(); - /* Test the max texture size */ - while(err && width>=1 && height>=1 /* && width>=64 && height>=64 */) - { - glTexImage2D (texTargetProxy, level, - internalFormat, - width, height, - border, format, type, nullptr); - if ( 0!=(err=GL_CHECK_ERROR_NORMAL() )) return err; - - glGetTexLevelParameteriv( texTargetProxy, level, GL_TEXTURE_WIDTH, avail_width); - glGetTexLevelParameteriv( texTargetProxy, level, GL_TEXTURE_HEIGHT, avail_height); - if ( 0!=(err=GL_CHECK_ERROR_NORMAL() )) return err; - - if ( (*avail_width) != width || (*avail_height) != height ) + // Test the max texture size + int err = 1; + GLenum const texTargetProxy = (target == GL_TEXTURE_RECTANGLE_ARB) ? GL_PROXY_TEXTURE_RECTANGLE_ARB : GL_PROXY_TEXTURE_2D; + while (err && width>=1 && height>=1 /* && width>=64 && height>=64 */) { - err=1; - - if(verbose) + glTexImage2D(texTargetProxy, level, + internalFormat, + width, height, + border, format, type, nullptr); + if (0 != (err=GL_CHECK_ERROR_NORMAL())) + return err; + + glGetTexLevelParameteriv(texTargetProxy, level, GL_TEXTURE_WIDTH, avail_width); + glGetTexLevelParameteriv(texTargetProxy, level, GL_TEXTURE_HEIGHT, avail_height); + if (0 != (err=GL_CHECK_ERROR_NORMAL())) + return err; + + if ((*avail_width != width) || (*avail_height != height)) { - osd_printf_warning("gl_texture_size_check: " - "TexImage2D(0x%X, %d, 0x%X, %d, %d, %d, 0x%X, 0x%X): returned size does not match: %dx%d\n", - (unsigned int)target, (int)level, (int)internalFormat, - (int)width, (int)height, (int)border, (unsigned int)format, (unsigned int)type, - (int)*avail_width, (int)*avail_height); - } - - if ( (*avail_width) == width ) - height /= 2; - else if ( (*avail_height) == height ) - width /= 2; - else if (width > height) + err = 1; + + if (verbose) + { + osd_printf_warning("gl_texture_size_check: " + "TexImage2D(0x%X, %d, 0x%X, %d, %d, %d, 0x%X, 0x%X): returned size does not match: %dx%d\n", + (unsigned int)target, (int)level, (int)internalFormat, + width, height, border, (unsigned int)format, (unsigned int)type, + *avail_width, *avail_height); + } + + if (*avail_width == width) + height /= 2; + else if (*avail_height == height) width /= 2; - else + else if (width > height) + width /= 2; + else height /= 2; - if(verbose) + if (verbose) + { + osd_printf_verbose("gl_texture_size_check: trying [%dx%d] !\n", height, width); + } + } + else { - fprintf (stderr, "gl_texture_size_check: trying [%dx%d] !\n", (int)height, (int)width); + err = 0; } - } else { - err=0; - } } - if(!err) + if (!err) { - *avail_width = width; + *avail_width = width; *avail_height = height; } return err; } -static int delete_shader_tool( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader, int externalcall) +int gl_shader_tool::delete_shader_tool( + GLhandleARB *program, + GLhandleARB *vertex_shader, + GLhandleARB *fragment_shader, + bool externalcall) { - int res=0; + int res = 0; GL_CHECK_ERROR_QUIET(); - if(program!= nullptr && *program!=0) + if (program && *program!=0) { pfn_glDeleteObjectARB(*program); - if(externalcall) - { + if (externalcall) res |= GL_SHADER_CHECK(*program, GL_OBJECT_DELETE_STATUS_ARB); - } - *program=0; + *program = 0; } - if(vertex_shader!= nullptr && *vertex_shader!=0) + if (vertex_shader && *vertex_shader!=0) { pfn_glDeleteObjectARB(*vertex_shader); - if(externalcall) - { + if (externalcall) res |= GL_SHADER_CHECK(*vertex_shader, GL_OBJECT_DELETE_STATUS_ARB); - } *vertex_shader=0; } - if(fragment_shader!= nullptr && *fragment_shader!=0) + if (fragment_shader && *fragment_shader!=0) { pfn_glDeleteObjectARB(*fragment_shader); - if(externalcall) - { + if (externalcall) res |= GL_SHADER_CHECK(*fragment_shader, GL_OBJECT_DELETE_STATUS_ARB); - } *fragment_shader=0; } - return res; + return res; } -int gl_compile_shader_source( GLhandleARB *shader, GLenum type, const char * shader_source, int verbose ) +int gl_shader_tool::compile_source( + GLhandleARB *shader, + GLenum type, + const char *shader_source, + bool verbose) { - int err = 0; + int err = 0; - if(shader== nullptr || shader_source== nullptr) + if (!shader || !shader_source) { - if(shader== nullptr) + if (!shader) osd_printf_warning("error: gl_compile_shader_source: nullptr shader passed\n"); - if(shader_source== nullptr) + if (!shader_source) osd_printf_warning("error: gl_compile_shader_source: nullptr shader source passed\n"); return -1; } - *shader = pfn_glCreateShaderObjectARB(type); - err=GL_SHADER_CHECK(*shader, GL_OBJECT_TYPE_ARB); - if(err) goto errout; + *shader = pfn_glCreateShaderObjectARB(type); + err = GL_SHADER_CHECK(*shader, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; - pfn_glShaderSourceARB(*shader, 1, (const GLcharARB **)&shader_source, nullptr); + pfn_glShaderSourceARB(*shader, 1, (const GLcharARB **)&shader_source, nullptr); - pfn_glCompileShaderARB(*shader); - err=GL_SHADER_CHECK(*shader, GL_OBJECT_COMPILE_STATUS_ARB); - if(err) goto errout; + pfn_glCompileShaderARB(*shader); + err = GL_SHADER_CHECK(*shader, GL_OBJECT_COMPILE_STATUS_ARB); + if (err) + goto errout; - if(verbose) + if (verbose) osd_printf_warning( "<%s>\n", shader_source); return 0; errout: - if(*shader!=0) - { + if (*shader != 0) pfn_glDeleteObjectARB(*shader); - } + osd_printf_warning("failed to process shader: <%s>\n", shader_source); - return err; + return err; } -int gl_compile_shader_file( GLhandleARB *shader, GLenum type, const char * shader_file, int verbose ) +int gl_shader_tool::compile_file( + GLhandleARB *shader, + GLenum type, + const char *shader_file, + bool verbose) { - if(shader== nullptr || shader_file== nullptr) + if (!shader || !shader_file) { - if(shader== nullptr) + if (!shader) osd_printf_warning("error: gl_compile_shader_source: nullptr shader passed\n"); - if(shader_file== nullptr) + if (!shader_file) osd_printf_warning("error: gl_compile_shader_source: nullptr shader file passed\n"); return -1; } FILE *const file = fopen(shader_file, "r"); - if(!file) + if (!file) { osd_printf_warning("cannot open shader_file: %s\n", shader_file); return -1; @@ -399,15 +447,16 @@ int gl_compile_shader_file( GLhandleARB *shader, GLenum type, const char * shade memset(buffer, 0, buffer_len + 1); /* Load Shader Sources */ - for( int i = 0, c = 0; i<buffer_len && EOF!=(c=fgetc(file)); i++ ) - buffer[i]=(char)c; + for (int i = 0, c = 0; i<buffer_len && EOF!=(c=fgetc(file)); i++) + buffer[i] = char(c); fclose(file); - int const err=gl_compile_shader_source(shader, type, buffer, verbose); + int const err = compile_source(shader, type, buffer, verbose); free(buffer); - if(err) goto errout; + if (err) + goto errout; - if(verbose) + if (verbose) osd_printf_warning("shader file: %s\n", shader_file); return 0; @@ -418,180 +467,204 @@ errout: } -int gl_compile_shader_files( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader, - const char * vertex_shader_file, - const char * fragment_shader_file, - int verbose - ) +int gl_shader_tool::compile_files( + GLhandleARB *program, + GLhandleARB *vertex_shader, + GLhandleARB *fragment_shader, + const char * vertex_shader_file, + const char * fragment_shader_file, + bool verbose) { int err; if (!program) { - err=-1; + err = -1; osd_printf_warning("no program ptr passed\n"); goto errout; } if (!vertex_shader) { - err=-1; + err = -1; osd_printf_warning("no vertex_shader ptr passed\n"); goto errout; } if (!fragment_shader) { - err=-1; + err = -1; osd_printf_warning("no fragment_shader ptr passed\n"); goto errout; } - *program = pfn_glCreateProgramObjectARB(); - err=GL_SHADER_CHECK(*program, GL_OBJECT_TYPE_ARB); - if(err) goto errout; + *program = pfn_glCreateProgramObjectARB(); + err=GL_SHADER_CHECK(*program, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; - if(!vertex_shader_file) + if (!vertex_shader_file) { if (!(*vertex_shader)) { - err=-1; + err = -1; osd_printf_warning("no vertex_shader_file, nor vertex_shader id passed\n"); goto errout; } - err=GL_SHADER_CHECK(*vertex_shader, GL_OBJECT_TYPE_ARB); - if(err) goto errout; - } else { - err = gl_compile_shader_file(vertex_shader, GL_VERTEX_SHADER_ARB, vertex_shader_file, verbose); - if(err) return err; + err = GL_SHADER_CHECK(*vertex_shader, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; + } + else + { + err = compile_file(vertex_shader, GL_VERTEX_SHADER_ARB, vertex_shader_file, verbose); + if (err) + return err; } - pfn_glAttachObjectARB(*program, *vertex_shader); + pfn_glAttachObjectARB(*program, *vertex_shader); - if(!fragment_shader_file) + if (!fragment_shader_file) { if (!(*fragment_shader)) { - err=-1; + err = -1; osd_printf_warning("no fragment_shader_file, nor fragment_shader id passed\n"); goto errout; } - err=GL_SHADER_CHECK(*fragment_shader, GL_OBJECT_TYPE_ARB); - if(err) goto errout; - } else { - err = gl_compile_shader_file(fragment_shader, GL_FRAGMENT_SHADER_ARB, fragment_shader_file, verbose); - if(err) return err; + err = GL_SHADER_CHECK(*fragment_shader, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; + } + else + { + err = compile_file(fragment_shader, GL_FRAGMENT_SHADER_ARB, fragment_shader_file, verbose); + if (err) + return err; } - pfn_glAttachObjectARB(*program, *fragment_shader); + pfn_glAttachObjectARB(*program, *fragment_shader); - pfn_glLinkProgramARB(*program); - err=GL_SHADER_CHECK(*program, GL_OBJECT_LINK_STATUS_ARB); - if(err) goto errout; + pfn_glLinkProgramARB(*program); + err = GL_SHADER_CHECK(*program, GL_OBJECT_LINK_STATUS_ARB); + if (err) + goto errout; - pfn_glValidateProgramARB(*program); - err=GL_SHADER_CHECK(*program, GL_OBJECT_VALIDATE_STATUS_ARB); - if(err) goto errout; + pfn_glValidateProgramARB(*program); + err = GL_SHADER_CHECK(*program, GL_OBJECT_VALIDATE_STATUS_ARB); + if (err) + goto errout; - return 0; + return 0; errout: - delete_shader_tool(program, vertex_shader, fragment_shader, 0); - return err; + delete_shader_tool(program, vertex_shader, fragment_shader, false); + return err; } -int gl_compile_shader_sources( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader, - const GLcharARB * vertex_shader_source, - const GLcharARB * fragment_shader_source - ) +int gl_shader_tool::compile_sources( + GLhandleARB *program, + GLhandleARB *vertex_shader, + GLhandleARB *fragment_shader, + const GLcharARB * vertex_shader_source, + const GLcharARB * fragment_shader_source) { - int err = 0; + int err = 0; if (!program) { - err=-1; + err = -1; osd_printf_warning("no program ptr passed\n"); goto errout; } if (!vertex_shader) { - err=-1; + err = -1; osd_printf_warning("no vertex_shader ptr passed\n"); goto errout; } if (!fragment_shader) { - err=-1; + err = -1; osd_printf_warning("no fragment_shader ptr passed\n"); goto errout; } - *program = pfn_glCreateProgramObjectARB(); - err=GL_SHADER_CHECK(*program, GL_OBJECT_TYPE_ARB); - if(err) goto errout; + *program = pfn_glCreateProgramObjectARB(); + err = GL_SHADER_CHECK(*program, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; - if(!vertex_shader_source) + if (!vertex_shader_source) { if (!(*vertex_shader)) { - err=-1; + err = -1; osd_printf_warning("no vertex_shader_source nor a vertex_shader id passed\n"); goto errout; } - err=GL_SHADER_CHECK(*vertex_shader, GL_OBJECT_TYPE_ARB); - if(err) goto errout; + err = GL_SHADER_CHECK(*vertex_shader, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; } - if(!fragment_shader_source) + if (!fragment_shader_source) { if (!(*fragment_shader)) { - err=-1; + err = -1; osd_printf_warning("no fragment_shader_source nor a fragment_shader id passed\n"); goto errout; } - err=GL_SHADER_CHECK(*fragment_shader, GL_OBJECT_TYPE_ARB); - if(err) goto errout; + err = GL_SHADER_CHECK(*fragment_shader, GL_OBJECT_TYPE_ARB); + if (err) + goto errout; } - if(vertex_shader_source) + if (vertex_shader_source) { - err=gl_compile_shader_source(vertex_shader, GL_VERTEX_SHADER_ARB, vertex_shader_source, 0); - if(err) goto errout; + err = compile_source(vertex_shader, GL_VERTEX_SHADER_ARB, vertex_shader_source, false); + if (err) + goto errout; } - pfn_glAttachObjectARB(*program, *vertex_shader); + pfn_glAttachObjectARB(*program, *vertex_shader); - if(fragment_shader_source) + if (fragment_shader_source) { - err=gl_compile_shader_source(fragment_shader, GL_FRAGMENT_SHADER_ARB, fragment_shader_source, 0); - if(err) goto errout; + err = compile_source(fragment_shader, GL_FRAGMENT_SHADER_ARB, fragment_shader_source, false); + if (err) + goto errout; } - pfn_glAttachObjectARB(*program, *fragment_shader); + pfn_glAttachObjectARB(*program, *fragment_shader); - pfn_glLinkProgramARB(*program); - err=GL_SHADER_CHECK(*program, GL_OBJECT_LINK_STATUS_ARB); - if(err) { + pfn_glLinkProgramARB(*program); + err = GL_SHADER_CHECK(*program, GL_OBJECT_LINK_STATUS_ARB); + if (err) + { osd_printf_warning("failed to link program\n"); osd_printf_warning("vertex shader: <%s>\n", vertex_shader_source); osd_printf_warning("fragment shader: <%s>\n", fragment_shader_source); goto errout; } - pfn_glValidateProgramARB(*program); - err=GL_SHADER_CHECK(*program, GL_OBJECT_VALIDATE_STATUS_ARB); - if(err) { + pfn_glValidateProgramARB(*program); + err = GL_SHADER_CHECK(*program, GL_OBJECT_VALIDATE_STATUS_ARB); + if (err) + { osd_printf_warning("failed to validate program\n"); osd_printf_warning("vertex shader: <%s>\n", vertex_shader_source); osd_printf_warning("fragment shader: <%s>\n", fragment_shader_source); goto errout; } - return 0; + return 0; errout: - delete_shader_tool(program, vertex_shader, fragment_shader, 0); - return err; - + delete_shader_tool(program, vertex_shader, fragment_shader, false); + return err; } -int gl_delete_shader( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader ) +int gl_shader_tool::delete_shader( + GLhandleARB *program, + GLhandleARB *vertex_shader, + GLhandleARB *fragment_shader) { - return delete_shader_tool(program, vertex_shader, fragment_shader, 1); + return delete_shader_tool(program, vertex_shader, fragment_shader, true); } diff --git a/src/osd/modules/opengl/gl_shader_tool.h b/src/osd/modules/opengl/gl_shader_tool.h index 2e0160cf85d..28a84143b29 100644 --- a/src/osd/modules/opengl/gl_shader_tool.h +++ b/src/osd/modules/opengl/gl_shader_tool.h @@ -17,10 +17,14 @@ * GL_ARB_fragment_shader * */ +#ifndef MAME_OSD_OPENGL_GL_SHADER_TOOL_H +#define MAME_OSD_OPENGL_GL_SHADER_TOOL_H +#pragma once -#ifndef _GL_SHADER_TOOL_ -#define _GL_SHADER_TOOL_ +#include "osd_opengl.h" + +#include <stdint.h> #ifdef __cplusplus extern "C" { @@ -36,14 +40,10 @@ extern "C" { * */ -#include <stdint.h> - #ifndef GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES 1 #endif -#include "osd_opengl.h" - #if defined(SDLMAME_MACOSX) #ifndef APIENTRY @@ -55,80 +55,6 @@ extern "C" { #endif -typedef void * (APIENTRYP PFNGLGETPROCADDRESSOS)(const char *procName); - -/** - * YOU HAVE TO CALL THIS FUNCTION ONCE ! - * @return 0 - ok .. all shader ARB functions loaded - * otherwise !=0 - */ -int gl_shader_loadExtention(osd_gl_context *gl_ctx); - -enum GLSLCheckMode { - CHECK_QUIET, /* just return 0, if no error, otherwise the GL error code, no stderr output */ - CHECK_VERBOSE, /* same as CHECK_QUIET, but in the case of an error, use stderr to be verbose */ - CHECK_ALWAYS_VERBOSE /* always print out all information available */ -}; - -#define GL_CHECK_ERROR_VERBOSE() gl_check_error(CHECK_ALWAYS_VERBOSE,__FILE__,__LINE__) -#define GL_CHECK_ERROR_NORMAL() gl_check_error(CHECK_VERBOSE,__FILE__,__LINE__) -#define GL_CHECK_ERROR_QUIET() gl_check_error(CHECK_QUIET,__FILE__,__LINE__) -#define GL_CHECK_ERROR(m) gl_check_error(m,__FILE__,__LINE__) - -#define GL_SHADER_CHECK_ERROR_VERBOSE(o,q) gl_shader_check_error(o,q,CHECK_ALWAYS_VERBOSE,__FILE__,__LINE__) -#define GL_SHADER_CHECK_ERROR_NORMAL(o,q) gl_shader_check_error(o,q,CHECK_VERBOSE,__FILE__,__LINE__) -#define GL_SHADER_CHECK_ERROR_QUIET(o,q) gl_shader_check_error(o,q,CHECK_QUIET,__FILE__,__LINE__) -#define GL_SHADER_CHECK_ERROR(o,q,m) gl_shader_check_error(o,q,m,__FILE__,__LINE__) - -#ifdef GL_SHADER_TOOL_DEBUG - #define GL_SHADER_CHECK(o,q) GL_SHADER_CHECK_ERROR_VERBOSE(o,q) - #define GL_CHECK() GL_CHECK_ERROR_VERBOSE() -#else - #define GL_SHADER_CHECK(o,q) GL_SHADER_CHECK_ERROR_NORMAL(o,q) - #define GL_CHECK() GL_CHECK_ERROR_NORMAL() -#endif - -int gl_check_error(GLSLCheckMode m, const char *file, const int line); - -int gl_texture_check_size(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, - GLint border, GLenum format, GLenum type, - GLsizei *avail_width, GLsizei *avail_height, - int verbose); - -int gl_round_to_pow2(int v); - -/** - * @param obj_query Can be either GL_OBJECT_TYPE_ARB, GL_OBJECT_DELETE_STATUS_ARB, GL_OBJECT_COMPILE_STATUS_ARB, - * GL_OBJECT_LINK_STATUS_ARB, GL_OBJECT_VALIDATE_STATUS_ARB - * Should be used after the referring action, i.e. GL_OBJECT_DELETE_STATUS_ARB after a - * glDeleteObjectARB call, etc. - */ -int gl_shader_check_error(GLhandleARB obj, GLenum obj_query, GLSLCheckMode m, const char *file, const int line); - -int gl_compile_shader_file ( GLhandleARB *shader, GLenum type, const char * shader_file, int verbose ); -int gl_compile_shader_source( GLhandleARB *shader, GLenum type, const char * shader_source, int verbose ); - -/** - * you can pass either a valid shader_file, or a precompiled vertex_shader, - * this is true for both, vertex and fragment shaders. - */ -int gl_compile_shader_files( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader, - const char * vertex_shader_file, - const char * fragment_shader_file, - int verbose - ); - -/** - * you can pass either a valid shader_file, or a precompiled vertex_shader, - * this is true for both, vertex and fragment shaders. - */ -int gl_compile_shader_sources( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader, - const GLcharARB * vertex_shader_source, - const GLcharARB * fragment_shader_source - ); - -int gl_delete_shader( GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader ); - #if defined(SDLMAME_MACOSX) #ifndef GL_ARB_shader_objects typedef char GLcharARB; @@ -158,32 +84,118 @@ typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); #endif -extern PFNGLGETOBJECTPARAMETERIVARBPROC pfn_glGetObjectParameterivARB; -extern PFNGLGETINFOLOGARBPROC pfn_glGetInfoLogARB; -extern PFNGLDELETEOBJECTARBPROC pfn_glDeleteObjectARB; -extern PFNGLCREATESHADEROBJECTARBPROC pfn_glCreateShaderObjectARB; -extern PFNGLSHADERSOURCEARBPROC pfn_glShaderSourceARB; -extern PFNGLCOMPILESHADERARBPROC pfn_glCompileShaderARB; -extern PFNGLCREATEPROGRAMOBJECTARBPROC pfn_glCreateProgramObjectARB; -extern PFNGLATTACHOBJECTARBPROC pfn_glAttachObjectARB; -extern PFNGLLINKPROGRAMARBPROC pfn_glLinkProgramARB; -extern PFNGLVALIDATEPROGRAMARBPROC pfn_glValidateProgramARB; -extern PFNGLUSEPROGRAMOBJECTARBPROC pfn_glUseProgramObjectARB; -extern PFNGLGETUNIFORMLOCATIONARBPROC pfn_glGetUniformLocationARB; -extern PFNGLUNIFORM1FARBPROC pfn_glUniform1fARB; -extern PFNGLUNIFORM1IARBPROC pfn_glUniform1iARB; -extern PFNGLUNIFORM1FVARBPROC pfn_glUniform1fvARB; -extern PFNGLUNIFORM2FVARBPROC pfn_glUniform2fvARB; -extern PFNGLUNIFORM3FVARBPROC pfn_glUniform3fvARB; -extern PFNGLUNIFORM4FVARBPROC pfn_glUniform4fvARB; -extern PFNGLUNIFORM1IVARBPROC pfn_glUniform1ivARB; -extern PFNGLUNIFORM2IVARBPROC pfn_glUniform2ivARB; -extern PFNGLUNIFORM3IVARBPROC pfn_glUniform3ivARB; -extern PFNGLUNIFORM4IVARBPROC pfn_glUniform4ivARB; - +typedef void * (APIENTRYP PFNGLGETPROCADDRESSOS)(const char *procName); #ifdef __cplusplus } #endif +class gl_shader_tool +{ +public: + gl_shader_tool( +#if defined(USE_DISPATCH_GL) + osd_gl_dispatch *gld #endif + ); + + PFNGLGETOBJECTPARAMETERIVARBPROC pfn_glGetObjectParameterivARB; + PFNGLGETINFOLOGARBPROC pfn_glGetInfoLogARB; + PFNGLDELETEOBJECTARBPROC pfn_glDeleteObjectARB; + PFNGLCREATESHADEROBJECTARBPROC pfn_glCreateShaderObjectARB; + PFNGLSHADERSOURCEARBPROC pfn_glShaderSourceARB; + PFNGLCOMPILESHADERARBPROC pfn_glCompileShaderARB; + PFNGLCREATEPROGRAMOBJECTARBPROC pfn_glCreateProgramObjectARB; + PFNGLATTACHOBJECTARBPROC pfn_glAttachObjectARB; + PFNGLLINKPROGRAMARBPROC pfn_glLinkProgramARB; + PFNGLVALIDATEPROGRAMARBPROC pfn_glValidateProgramARB; + PFNGLUSEPROGRAMOBJECTARBPROC pfn_glUseProgramObjectARB; + PFNGLGETUNIFORMLOCATIONARBPROC pfn_glGetUniformLocationARB; + PFNGLUNIFORM1FARBPROC pfn_glUniform1fARB; + PFNGLUNIFORM1IARBPROC pfn_glUniform1iARB; + PFNGLUNIFORM1FVARBPROC pfn_glUniform1fvARB; + PFNGLUNIFORM2FVARBPROC pfn_glUniform2fvARB; + PFNGLUNIFORM3FVARBPROC pfn_glUniform3fvARB; + PFNGLUNIFORM4FVARBPROC pfn_glUniform4fvARB; + PFNGLUNIFORM1IVARBPROC pfn_glUniform1ivARB; + PFNGLUNIFORM2IVARBPROC pfn_glUniform2ivARB; + PFNGLUNIFORM3IVARBPROC pfn_glUniform3ivARB; + PFNGLUNIFORM4IVARBPROC pfn_glUniform4ivARB; + + /** + * YOU HAVE TO CALL THIS FUNCTION ONCE ! + * @return true - ok .. all shader ARB functions loaded + * otherwise false + */ + bool load_extension(osd_gl_context &gl_ctx); + + int texture_check_size( + GLenum target, + GLint level, + GLint internalFormat, + GLsizei width, + GLsizei height, + GLint border, + GLenum format, + GLenum type, + GLsizei *avail_width, + GLsizei *avail_height, + bool verbose); + +protected: + int compile_file(GLhandleARB *shader, GLenum type, const char *shader_file, bool verbose); + int compile_source(GLhandleARB *shader, GLenum type, const char *shader_source, bool verbose); + + /** + * you can pass either a valid shader_file, or a precompiled vertex_shader, + * this is true for both, vertex and fragment shaders. + */ + int compile_files( + GLhandleARB *program, + GLhandleARB *vertex_shader, + GLhandleARB *fragment_shader, + const char *vertex_shader_file, + const char *fragment_shader_file, + bool verbose); + + /** + * you can pass either a valid shader_file, or a precompiled vertex_shader, + * this is true for both, vertex and fragment shaders. + */ + int compile_sources( + GLhandleARB *program, + GLhandleARB *vertex_shader, + GLhandleARB *fragment_shader, + const GLcharARB *vertex_shader_source, + const GLcharARB *fragment_shader_source); + + int delete_shader(GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader); + +#if defined(USE_DISPATCH_GL) + osd_gl_dispatch *const gl_dispatch; // name is magic, can't be changed +#endif + +private: + enum GLSLCheckMode + { + CHECK_QUIET, // just return 0, if no error, otherwise the GL error code, no stderr output + CHECK_VERBOSE, // same as CHECK_QUIET, but in the case of an error, use stderr to be verbose + CHECK_ALWAYS_VERBOSE // always print out all information available + }; + + int check_error(GLSLCheckMode m, const char *file, const int line); + + /** + * @param obj_query Can be either GL_OBJECT_TYPE_ARB, GL_OBJECT_DELETE_STATUS_ARB, GL_OBJECT_COMPILE_STATUS_ARB, + * GL_OBJECT_LINK_STATUS_ARB, GL_OBJECT_VALIDATE_STATUS_ARB + * Should be used after the referring action, i.e. GL_OBJECT_DELETE_STATUS_ARB after a + * glDeleteObjectARB call, etc. + */ + int check_error(GLhandleARB obj, GLenum obj_query, GLSLCheckMode m, const char *file, const int line); + + int delete_shader_tool(GLhandleARB *program, GLhandleARB *vertex_shader, GLhandleARB *fragment_shader, bool externalcall); +}; + +int gl_round_to_pow2(int v); + +#endif // MAME_OSD_OPENGL_GL_SHADER_TOOL_H diff --git a/src/osd/modules/opengl/osd_opengl.h b/src/osd/modules/opengl/osd_opengl.h index d3e2a758d73..712b3b880b4 100644 --- a/src/osd/modules/opengl/osd_opengl.h +++ b/src/osd/modules/opengl/osd_opengl.h @@ -36,35 +36,38 @@ #include <OpenGL/gl.h> #include <OpenGL/glext.h> #else - #include <SDL2/SDL_version.h> - - #if (SDL_VERSION_ATLEAST(1,2,10)) - #if defined(SDLMAME_WIN32) - // Avoid that winnt.h (included via sdl_opengl.h, windows.h, windef.h includes intrin.h - #define __INTRIN_H_ - #endif - #include <SDL2/SDL_opengl.h> - #endif + #include <SDL2/SDL_version.h> + #include <SDL2/SDL_opengl.h> #endif class osd_gl_context { public: - osd_gl_context() { } - virtual ~osd_gl_context() { } - virtual void MakeCurrent() = 0; - virtual const char *LastErrorMsg() = 0; - virtual void *getProcAddress(const char *proc) = 0; + virtual ~osd_gl_context() = default; + + virtual explicit operator bool() const = 0; + + virtual void make_current() = 0; + virtual const char *last_error_message() = 0; + virtual void *get_proc_address(const char *proc) = 0; + /* * 0 for immediate updates, * 1 for updates synchronized with the vertical retrace, * -1 for late swap tearing * - * returns -1 if swap interval is not supported - * + * returns false if swap interval is not supported */ - virtual int SetSwapInterval(const int swap) = 0; - virtual void SwapBuffer() = 0; + virtual bool set_swap_interval(const int swap) = 0; + + virtual void swap_buffer() = 0; + + template <typename T> + bool get_proc_address(T &ptr, const char *name) + { + ptr = T(uintptr_t(get_proc_address(name))); + return bool(ptr); + } }; @@ -100,11 +103,6 @@ #undef OSD_GL #undef OSD_GL_UNUSED -#ifdef _MSC_VER - extern "C" osd_gl_dispatch *gl_dispatch; -#endif - extern osd_gl_dispatch *gl_dispatch; - /* * Use gl_mangle to map function names */ @@ -127,67 +125,67 @@ * ***************************************************************/ - #define glAccum MANGLE(Accum) - #define glActiveStencilFaceEXT MANGLE(ActiveStencilFaceEXT) - #define glActiveTextureARB MANGLE(ActiveTextureARB) - #define glActiveTexture MANGLE(ActiveTexture) - #define glAlphaFragmentOp1ATI MANGLE(AlphaFragmentOp1ATI) - #define glAlphaFragmentOp2ATI MANGLE(AlphaFragmentOp2ATI) - #define glAlphaFragmentOp3ATI MANGLE(AlphaFragmentOp3ATI) - #define glAlphaFunc MANGLE(AlphaFunc) - #define glApplyTextureEXT MANGLE(ApplyTextureEXT) - #define glAreProgramsResidentNV MANGLE(AreProgramsResidentNV) + #define glAccum MANGLE(Accum) + #define glActiveStencilFaceEXT MANGLE(ActiveStencilFaceEXT) + #define glActiveTextureARB MANGLE(ActiveTextureARB) + #define glActiveTexture MANGLE(ActiveTexture) + #define glAlphaFragmentOp1ATI MANGLE(AlphaFragmentOp1ATI) + #define glAlphaFragmentOp2ATI MANGLE(AlphaFragmentOp2ATI) + #define glAlphaFragmentOp3ATI MANGLE(AlphaFragmentOp3ATI) + #define glAlphaFunc MANGLE(AlphaFunc) + #define glApplyTextureEXT MANGLE(ApplyTextureEXT) + #define glAreProgramsResidentNV MANGLE(AreProgramsResidentNV) #define glAreTexturesResidentEXT MANGLE(AreTexturesResidentEXT) - #define glAreTexturesResident MANGLE(AreTexturesResident) - #define glArrayElementEXT MANGLE(ArrayElementEXT) - #define glArrayElement MANGLE(ArrayElement) - #define glArrayObjectATI MANGLE(ArrayObjectATI) - #define glAsyncMarkerSGIX MANGLE(AsyncMarkerSGIX) - #define glAttachObjectARB MANGLE(AttachObjectARB) - #define glAttachShader MANGLE(AttachShader) + #define glAreTexturesResident MANGLE(AreTexturesResident) + #define glArrayElementEXT MANGLE(ArrayElementEXT) + #define glArrayElement MANGLE(ArrayElement) + #define glArrayObjectATI MANGLE(ArrayObjectATI) + #define glAsyncMarkerSGIX MANGLE(AsyncMarkerSGIX) + #define glAttachObjectARB MANGLE(AttachObjectARB) + #define glAttachShader MANGLE(AttachShader) #define glBeginFragmentShaderATI MANGLE(BeginFragmentShaderATI) - #define glBegin MANGLE(Begin) - #define glBeginOcclusionQueryNV MANGLE(BeginOcclusionQueryNV) - #define glBeginQueryARB MANGLE(BeginQueryARB) - #define glBeginQuery MANGLE(BeginQuery) - #define glBeginVertexShaderEXT MANGLE(BeginVertexShaderEXT) - #define glBindAttribLocationARB MANGLE(BindAttribLocationARB) - #define glBindAttribLocation MANGLE(BindAttribLocation) - #define glBindBufferARB MANGLE(BindBufferARB) - #define glBindBuffer MANGLE(BindBuffer) - #define glBindFragmentShaderATI MANGLE(BindFragmentShaderATI) - #define glBindFramebufferEXT MANGLE(BindFramebufferEXT) - #define glBindLightParameterEXT MANGLE(BindLightParameterEXT) + #define glBegin MANGLE(Begin) + #define glBeginOcclusionQueryNV MANGLE(BeginOcclusionQueryNV) + #define glBeginQueryARB MANGLE(BeginQueryARB) + #define glBeginQuery MANGLE(BeginQuery) + #define glBeginVertexShaderEXT MANGLE(BeginVertexShaderEXT) + #define glBindAttribLocationARB MANGLE(BindAttribLocationARB) + #define glBindAttribLocation MANGLE(BindAttribLocation) + #define glBindBufferARB MANGLE(BindBufferARB) + #define glBindBuffer MANGLE(BindBuffer) + #define glBindFragmentShaderATI MANGLE(BindFragmentShaderATI) + #define glBindFramebufferEXT MANGLE(BindFramebufferEXT) + #define glBindLightParameterEXT MANGLE(BindLightParameterEXT) #define glBindMaterialParameterEXT MANGLE(BindMaterialParameterEXT) - #define glBindParameterEXT MANGLE(BindParameterEXT) - #define glBindProgramARB MANGLE(BindProgramARB) - #define glBindProgramNV MANGLE(BindProgramNV) - #define glBindRenderbufferEXT MANGLE(BindRenderbufferEXT) + #define glBindParameterEXT MANGLE(BindParameterEXT) + #define glBindProgramARB MANGLE(BindProgramARB) + #define glBindProgramNV MANGLE(BindProgramNV) + #define glBindRenderbufferEXT MANGLE(BindRenderbufferEXT) #define glBindTexGenParameterEXT MANGLE(BindTexGenParameterEXT) - #define glBindTextureEXT MANGLE(BindTextureEXT) - #define glBindTexture MANGLE(BindTexture) - #define glBindTextureUnitParameterEXT MANGLE(BindTextureUnitParameterEXT) - #define glBindVertexArrayAPPLE MANGLE(BindVertexArrayAPPLE) - #define glBindVertexShaderEXT MANGLE(BindVertexShaderEXT) - #define glBinormal3bEXT MANGLE(Binormal3bEXT) - #define glBinormal3bvEXT MANGLE(Binormal3bvEXT) - #define glBinormal3dEXT MANGLE(Binormal3dEXT) - #define glBinormal3dvEXT MANGLE(Binormal3dvEXT) - #define glBinormal3fEXT MANGLE(Binormal3fEXT) - #define glBinormal3fvEXT MANGLE(Binormal3fvEXT) - #define glBinormal3iEXT MANGLE(Binormal3iEXT) - #define glBinormal3ivEXT MANGLE(Binormal3ivEXT) - #define glBinormal3sEXT MANGLE(Binormal3sEXT) - #define glBinormal3svEXT MANGLE(Binormal3svEXT) - #define glBinormalPointerEXT MANGLE(BinormalPointerEXT) - #define glBitmap MANGLE(Bitmap) - #define glBlendColorEXT MANGLE(BlendColorEXT) - #define glBlendColor MANGLE(BlendColor) - #define glBlendEquationEXT MANGLE(BlendEquationEXT) - #define glBlendEquation MANGLE(BlendEquation) + #define glBindTextureEXT MANGLE(BindTextureEXT) + #define glBindTexture MANGLE(BindTexture) + #define glBindTextureUnitParameterEXT MANGLE(BindTextureUnitParameterEXT) + #define glBindVertexArrayAPPLE MANGLE(BindVertexArrayAPPLE) + #define glBindVertexShaderEXT MANGLE(BindVertexShaderEXT) + #define glBinormal3bEXT MANGLE(Binormal3bEXT) + #define glBinormal3bvEXT MANGLE(Binormal3bvEXT) + #define glBinormal3dEXT MANGLE(Binormal3dEXT) + #define glBinormal3dvEXT MANGLE(Binormal3dvEXT) + #define glBinormal3fEXT MANGLE(Binormal3fEXT) + #define glBinormal3fvEXT MANGLE(Binormal3fvEXT) + #define glBinormal3iEXT MANGLE(Binormal3iEXT) + #define glBinormal3ivEXT MANGLE(Binormal3ivEXT) + #define glBinormal3sEXT MANGLE(Binormal3sEXT) + #define glBinormal3svEXT MANGLE(Binormal3svEXT) + #define glBinormalPointerEXT MANGLE(BinormalPointerEXT) + #define glBitmap MANGLE(Bitmap) + #define glBlendColorEXT MANGLE(BlendColorEXT) + #define glBlendColor MANGLE(BlendColor) + #define glBlendEquationEXT MANGLE(BlendEquationEXT) + #define glBlendEquation MANGLE(BlendEquation) #define glBlendEquationSeparateATI MANGLE(BlendEquationSeparateATI) #define glBlendEquationSeparateEXT MANGLE(BlendEquationSeparateEXT) - #define glBlendEquationSeparate MANGLE(BlendEquationSeparate) + #define glBlendEquationSeparate MANGLE(BlendEquationSeparate) #define glBlendFunc MANGLE(BlendFunc) #define glBlendFuncSeparateEXT MANGLE(BlendFuncSeparateEXT) #define glBlendFuncSeparateINGR MANGLE(BlendFuncSeparateINGR) diff --git a/src/osd/modules/osdmodule.cpp b/src/osd/modules/osdmodule.cpp index 4f2cf079e3b..0fb43d4331a 100644 --- a/src/osd/modules/osdmodule.cpp +++ b/src/osd/modules/osdmodule.cpp @@ -1,11 +1,14 @@ // license:BSD-3-Clause // copyright-holders:Couriersud /* - * osdmodule.c + * osdmodule.cpp * */ #include "modules/osdmodule.h" + +#include "emucore.h" + #include "osdcore.h" #include <algorithm> @@ -47,20 +50,62 @@ osd_module *osd_module_manager::get_module_generic(const char *type, const char return nullptr; } -osd_module *osd_module_manager::select_module(const char *type, const char *name) +osd_module &osd_module_manager::select_module(osd_interface &osd, const osd_options &options, const char *type, const char *name) { - osd_module *m = get_module_generic(type, name); + // see if a module of this type has already been already selected + for (osd_module &existing : m_selected) + { + if (existing.type() == type) + { + osd_printf_warning( + "Attempt to select %s module %s after selecting module %s\n", + type, + (name && *name) ? name : "<default>", + existing.name()); + return existing; + } + } - // FIXME: check if already exists! + // try to start the selected module + osd_module *const m = get_module_generic(type, name); if (m) - m_selected.emplace_back(*m); - return m; -} + { + //osd_printf_verbose("Attempting to initialize %s module %s\n", type, m->name()); + if (!m->init(osd, options)) + { + m_selected.emplace_back(*m); + return *m; + } + osd_printf_verbose("Initializing %s module %s failed\n", type, m->name()); + } + else + { + osd_printf_verbose("Could not find %s module %s\n", type, name ? name : "<default>"); + } -void osd_module_manager::init(const osd_options &options) -{ - for (osd_module &m : m_selected) - m.init(options); + // walk down the list until something works + for (auto const &fallback : m_modules) + { + if ((fallback->type() == type) && (fallback.get() != m)) + { + osd_printf_verbose("Attempting to initialize %s module %s\n", type, fallback->name()); + if (!fallback->init(osd, options)) + { + osd_printf_warning( + m + ? "Initializing %s module %s failed, falling back to %s\n" + : "Could not find %s module %s, falling back to %s\n", + type, + m ? m->name() : (name && *name) ? name : "<default>", + fallback->name()); + m_selected.emplace_back(*fallback); + return *fallback; + } + } + } + + // can't deal with absence of a module, and at least the "none" module should have worked + throw emu_fatalerror("All %s modules failed to initialize", type); } void osd_module_manager::exit() @@ -83,12 +128,13 @@ int osd_module_manager::get_module_index(const char *type, const char *name) con return -1; } -void osd_module_manager::get_module_names(const char *type, const int max, int &num, const char *names[]) const +std::vector<std::string_view> osd_module_manager::get_module_names(const char *type) const { - num = 0; - for (int i = 0; (m_modules.size() > i) && (max > num); i++) + std::vector<std::string_view> result; + for (auto &m : m_modules) { - if (m_modules[i]->type() == type) - names[num++] = m_modules[i]->name().c_str(); + if (m->type() == type) + result.emplace_back(m->name()); } + return result; } diff --git a/src/osd/modules/osdmodule.h b/src/osd/modules/osdmodule.h index 1c662417601..b690f874706 100644 --- a/src/osd/modules/osdmodule.h +++ b/src/osd/modules/osdmodule.h @@ -15,6 +15,7 @@ #include <functional> #include <memory> #include <string> +#include <string_view> #include <vector> @@ -22,9 +23,13 @@ // TYPE DEFINITIONS //============================================================ +class osd_interface; class osd_options; -// ======================> osd_module + +//============================================================ +// TYPE DEFINITIONS +//============================================================ class osd_module { @@ -36,7 +41,7 @@ public: virtual bool probe() { return true; } - virtual int init(const osd_options &options) = 0; + virtual int init(osd_interface &osd, const osd_options &options) = 0; virtual void exit() { } protected: @@ -48,9 +53,11 @@ private: std::string const m_type; }; + // a module_type is simply a pointer to its alloc function typedef std::unique_ptr<osd_module> (*module_type)(); + // this template function creates a stub which constructs a module template <class ModuleClass> std::unique_ptr<osd_module> module_creator() @@ -58,6 +65,7 @@ std::unique_ptr<osd_module> module_creator() return std::unique_ptr<osd_module>(new ModuleClass); } + class osd_module_manager { public: @@ -70,16 +78,14 @@ public: osd_module *get_module_generic(const char *type, const char *name); template<class C> - C select_module(const char *type, const char *name = "") + C &select_module(osd_interface &osd, const osd_options &options, const char *type, const char *name = "") { - return dynamic_cast<C>(select_module(type, name)); + return dynamic_cast<C &>(select_module(osd, options, type, name)); } - osd_module *select_module(const char *type, const char *name = ""); + osd_module &select_module(osd_interface &osd, const osd_options &options, const char *type, const char *name = ""); - void get_module_names(const char *type, const int max, int &num, const char *names[]) const; - - void init(const osd_options &options); + std::vector<std::string_view> get_module_names(const char *type) const; void exit(); @@ -90,18 +96,20 @@ private: std::vector<std::reference_wrapper<osd_module> > m_selected; }; + #define MODULE_DEFINITION(mod_id, mod_class) \ - extern const module_type mod_id ; \ - const module_type mod_id = &module_creator< mod_class >; + extern const module_type mod_id ; \ + const module_type mod_id = &module_creator<mod_class>; #define MODULE_NOT_SUPPORTED(mod_class, mod_type, mod_name) \ - class mod_class : public osd_module { \ - public: \ - mod_class () : osd_module(mod_type, mod_name) { } \ - virtual ~mod_class() { } \ - virtual int init(const osd_options &options) override { return -1; } \ - virtual bool probe() override { return false; } \ - }; + class mod_class : public osd_module \ + { \ + public: \ + mod_class () : osd_module(mod_type, mod_name) { } \ + virtual ~mod_class() { } \ + virtual int init(osd_interface &osd, const osd_options &options) override { return -1; } \ + virtual bool probe() override { return false; } \ + }; #endif /* MAME_OSD_MODULES_OSDMODULE_H */ diff --git a/src/osd/modules/osdwindow.cpp b/src/osd/modules/osdwindow.cpp index bc20676cbad..38b86e652e0 100644 --- a/src/osd/modules/osdwindow.cpp +++ b/src/osd/modules/osdwindow.cpp @@ -8,26 +8,23 @@ #include "emu.h" #include "osdwindow.h" -#include "modules/lib/osdobj_common.h" -#include "modules/monitor/monitor_module.h" - -#include "render/drawnone.h" -#include "render/drawbgfx.h" -#if (USE_OPENGL) -#include "render/drawogl.h" -#endif -#if defined(OSD_WINDOWS) -#include "render/drawgdi.h" -#include "render/drawd3d.h" -#elif defined(OSD_SDL) -#include "render/draw13.h" -#include "render/drawsdl.h" -#endif - -osd_window::osd_window(running_machine &machine, int index, std::shared_ptr<osd_monitor_info> monitor, const osd_window_config &config) : -#ifdef OSD_WINDOWS - m_dc(nullptr), m_resize_state(0), -#endif + +// osd/modules +#include "lib/osdobj_common.h" +#include "monitor/monitor_module.h" +#include "render/render_module.h" + +// emu +#include "main.h" +#include "render.h" + + +osd_window::osd_window( + running_machine &machine, + render_module &renderprovider, + int index, + const std::shared_ptr<osd_monitor_info> &monitor, + const osd_window_config &config) : m_target(nullptr), m_primlist(nullptr), m_win_config(config), @@ -35,22 +32,26 @@ osd_window::osd_window(running_machine &machine, int index, std::shared_ptr<osd_ m_fullscreen(false), m_prescale(1), m_machine(machine), + m_renderprovider(renderprovider), m_monitor(std::move(monitor)), m_renderer(nullptr), - m_main(nullptr), m_title( util::string_format( (video_config.numscreens > 1) ? "%3$s [%4$s] screen %5$d - %1$s %2$s (%6$s%7$sP%8$d)" : "%3$s [%4$s] - %1$s %2$s (%6$s%7$sP%8$d)", - emulator_info::get_appname(), - emulator_info::get_bare_build_version(), - machine.system().type.fullname(), - machine.system().name, - index, - (sizeof(int) == sizeof(void *)) ? "I" : "", - (sizeof(long) == sizeof(void *)) ? "L" : (sizeof(long long) == sizeof(void *)) ? "LL" : "", - sizeof(void *) * 8)) + emulator_info::get_appname(), + emulator_info::get_bare_build_version(), + machine.system().type.fullname(), + machine.system().name, + index, + (sizeof(int) == sizeof(void *)) ? "I" : "", + (sizeof(long) == sizeof(void *)) ? "L" : (sizeof(long long) == sizeof(void *)) ? "LL" : "", + sizeof(void *) * 8)) +{ +} + +osd_window::~osd_window() { } @@ -76,36 +77,6 @@ bool osd_window::keepaspect() const return false; } -std::unique_ptr<osd_renderer> osd_renderer::make_for_type(int mode, std::shared_ptr<osd_window> window, int extra_flags) -{ - switch(mode) - { -#if defined(OSD_WINDOWS) - case VIDEO_MODE_NONE: - return std::make_unique<renderer_none>(window); -#endif - case VIDEO_MODE_BGFX: - return std::make_unique<renderer_bgfx>(window); -#if (USE_OPENGL) - case VIDEO_MODE_OPENGL: - return std::make_unique<renderer_ogl>(window); -#endif -#if defined(OSD_WINDOWS) - case VIDEO_MODE_GDI: - return std::make_unique<renderer_gdi>(window); - case VIDEO_MODE_D3D: - return std::make_unique<renderer_d3d9>(window); -#elif defined(OSD_SDL) - case VIDEO_MODE_SDL2ACCEL: - return std::make_unique<renderer_sdl2>(window, extra_flags); - case VIDEO_MODE_SOFT: - return std::make_unique<renderer_sdl1>(window, extra_flags); -#endif - default: - return nullptr; - } -} - std::shared_ptr<osd_monitor_info> osd_window::monitor_from_rect(const osd_rect *proposed) const { std::shared_ptr<osd_monitor_info> monitor; @@ -122,9 +93,6 @@ std::shared_ptr<osd_monitor_info> osd_window::monitor_from_rect(const osd_rect * void osd_window::create_target() { - // add us to the list - osd_common_t::s_window_list.push_back(shared_from_this()); - // load the layout m_target = m_machine.render().target_alloc(); @@ -133,6 +101,21 @@ void osd_window::create_target() set_starting_view(m_index, options.view(), options.view(m_index)); } +bool osd_window::renderer_interactive() const +{ + return m_renderprovider.is_interactive(); +} + +bool osd_window::renderer_sdl_needs_opengl() const +{ + return m_renderprovider.sdl_needs_opengl(); +} + +void osd_window::renderer_create() +{ + m_renderer = m_renderprovider.create(*this); +} + void osd_window::set_starting_view(int index, const char *defview, const char *view) { // choose non-auto over auto @@ -148,9 +131,6 @@ void osd_window::set_starting_view(int index, const char *defview, const char *v void osd_window::destroy() { - // remove us from the list - osd_common_t::s_window_list.remove(shared_from_this()); - // free the textures etc complete_destroy(); diff --git a/src/osd/modules/osdwindow.h b/src/osd/modules/osdwindow.h index f0f3813e407..f3d39878b5e 100644 --- a/src/osd/modules/osdwindow.h +++ b/src/osd/modules/osdwindow.h @@ -6,13 +6,19 @@ // //============================================================ -#ifndef __OSDWINDOW__ -#define __OSDWINDOW__ +#ifndef MAME_OSD_MODULES_OSDWINDOW_H +#define MAME_OSD_MODULES_OSDWINDOW_H + +#pragma once #include "emucore.h" #include "osdhelper.h" #include "../frontend/mame/ui/menuitem.h" +#include <memory> +#include <string> +#include <vector> + // standard windows headers #ifdef OSD_WINDOWS #include <windows.h> @@ -20,32 +26,16 @@ #include <mmsystem.h> #endif -#ifdef OSD_SDL -// forward declaration -struct SDL_Window; -#endif - //============================================================ // TYPE DEFINITIONS //============================================================ -class osd_options; +class osd_monitor_info; +class render_module; class render_primitive_list; -enum -{ - VIDEO_MODE_NONE = 0, - VIDEO_MODE_GDI, - VIDEO_MODE_BGFX, -#if defined(USE_OPENGL) && USE_OPENGL - VIDEO_MODE_OPENGL, -#endif - VIDEO_MODE_SDL2ACCEL, - VIDEO_MODE_D3D, - VIDEO_MODE_SOFT, +class osd_renderer; - VIDEO_MODE_COUNT -}; class osd_window_config { @@ -59,15 +49,11 @@ public: int refresh; // decoded refresh }; -class osd_renderer; -class osd_monitor_info; -class osd_window : public std::enable_shared_from_this<osd_window> +class osd_window { public: - osd_window(running_machine &machine, int index, std::shared_ptr<osd_monitor_info> monitor, const osd_window_config &config); - - virtual ~osd_window() { } + virtual ~osd_window(); render_target *target() const { return m_target; } int fullscreen() const { return m_fullscreen; } @@ -76,11 +62,7 @@ public: bool has_renderer() const { return m_renderer != nullptr; } osd_renderer &renderer() const { return *m_renderer; } - void set_renderer(std::unique_ptr<osd_renderer> renderer) - { - m_renderer = std::move(renderer); - } - void renderer_reset() { m_renderer.reset(); } + void renderer_reset() { m_renderer.reset(); } // public because OSD object calls it directly during teardown int index() const { return m_index; } int prescale() const { return m_prescale; } @@ -96,9 +78,6 @@ public: osd_monitor_info *monitor() const { return m_monitor.get(); } std::shared_ptr<osd_monitor_info> monitor_from_rect(const osd_rect *proposed) const; - std::shared_ptr<osd_window> main_window() const { return m_main; } - void set_main_window(std::shared_ptr<osd_window> main) { m_main = main; } - void create_target(); void destroy(); @@ -114,18 +93,21 @@ public: virtual void update() = 0; virtual void complete_destroy() = 0; -#if defined(OSD_WINDOWS) - virtual bool win_has_menu() = 0; -#endif +protected: + osd_window( + running_machine &machine, + render_module &renderprovider, + int index, + const std::shared_ptr<osd_monitor_info> &monitor, + const osd_window_config &config); + + bool renderer_interactive() const; + bool renderer_sdl_needs_opengl() const; + void renderer_create(); private: void set_starting_view(int index, const char *defview, const char *view); -public: // TODO: make these private -#ifdef OSD_WINDOWS - HDC m_dc; // only used by GDI renderer! - int m_resize_state; -#endif private: render_target *m_target; public: @@ -136,33 +118,32 @@ private: protected: bool m_fullscreen; int m_prescale; + private: - running_machine &m_machine; - std::shared_ptr<osd_monitor_info> m_monitor; - std::unique_ptr<osd_renderer> m_renderer; - std::shared_ptr<osd_window> m_main; - const std::string m_title; + running_machine &m_machine; + render_module &m_renderprovider; + std::shared_ptr<osd_monitor_info> m_monitor; + std::unique_ptr<osd_renderer> m_renderer; + const std::string m_title; }; template <class TWindowHandle> class osd_window_t : public osd_window { -private: - TWindowHandle m_platform_window; public: - osd_window_t(running_machine &machine, int index, std::shared_ptr<osd_monitor_info> monitor, const osd_window_config &config) - : osd_window(machine, index, std::move(monitor), config), - m_platform_window(nullptr) - { - } - TWindowHandle platform_window() const { return m_platform_window; } +protected: + using osd_window::osd_window; + void set_platform_window(TWindowHandle window) { assert(window == nullptr || m_platform_window == nullptr); m_platform_window = window; } + +private: + TWindowHandle m_platform_window = nullptr; }; @@ -172,31 +153,13 @@ public: /* Generic flags */ static const int FLAG_NONE = 0x0000; - static const int FLAG_NEEDS_OPENGL = 0x0001; - static const int FLAG_HAS_VECTOR_SCREEN = 0x0002; + static const int FLAG_HAS_VECTOR_SCREEN = 0x0001; - /* SDL 1.2 flags */ - static const int FLAG_NEEDS_DOUBLEBUF = 0x0100; - static const int FLAG_NEEDS_ASYNCBLIT = 0x0200; - - osd_renderer(std::shared_ptr<osd_window> window, const int flags) - : m_sliders_dirty(false), m_window(window), m_flags(flags) - { } + osd_renderer(osd_window &window) : m_sliders_dirty(false), m_window(window), m_flags(0) { } virtual ~osd_renderer() { } - std::shared_ptr<osd_window> assert_window() const - { - auto win = m_window.lock(); - if (!win) - throw emu_fatalerror("osd_renderer::assert_window: Window weak_ptr is not available!"); - return win; - } - - std::shared_ptr<osd_window> try_getwindow() const - { - return m_window.lock(); - } + osd_window &window() const { return m_window; } bool has_flags(const int flag) const { return ((m_flags & flag)) == flag; } void set_flags(int aflag) { m_flags |= aflag; } @@ -218,8 +181,6 @@ public: virtual void toggle_fsfx() { } virtual bool sliders_dirty() { return m_sliders_dirty; } - static std::unique_ptr<osd_renderer> make_for_type(int mode, std::shared_ptr<osd_window> window, int extra_flags = FLAG_NONE); - protected: virtual void build_slider_list() { } @@ -229,7 +190,7 @@ protected: std::vector<ui::menu_item> m_sliders; private: - std::weak_ptr<osd_window> m_window; + osd_window &m_window; int m_flags; }; @@ -241,10 +202,6 @@ private: #define MAX_VIDEO_WINDOWS (4) -#define VIDEO_SCALE_MODE_NONE (0) - -#define GLSL_SHADER_MAX 10 - //============================================================ // TYPE DEFINITIONS @@ -258,35 +215,19 @@ struct osd_video_config int numscreens; // number of screens // hardware options - int mode; // output mode int waitvsync; // spin until vsync int syncrefresh; // sync only to refresh rate int switchres; // switch resolutions // d3d, accel, opengl int filter; // enable filtering - //int filter; // enable filtering, disabled if glsl_filter>0 - - // OpenGL options - int glsl; - int glsl_filter; // glsl filtering, >0 disables filter - char * glsl_shader_mamebm[GLSL_SHADER_MAX]; // custom glsl shader set, mame bitmap - int glsl_shader_mamebm_num; // custom glsl shader set number, mame bitmap - char * glsl_shader_scrn[GLSL_SHADER_MAX]; // custom glsl shader set, screen bitmap - int glsl_shader_scrn_num; // custom glsl shader number, screen bitmap - int pbo; - int vbo; - int allowtexturerect; // allow GL_ARB_texture_rectangle, default: no - int forcepow2texture; // force power of two textures, default: no - - // dd, d3d + + // d3d int triplebuf; // triple buffer //============================================================ // SDL - options //============================================================ - int novideo; // don't draw, for pure CPU benchmarking - int centerh; int centerv; @@ -298,9 +239,6 @@ struct osd_video_config // X11 options int restrictonemonitor; // in fullscreen, confine to Xinerama monitor 0 - - // YUV options - int scale_mode; }; //============================================================ @@ -309,4 +247,4 @@ struct osd_video_config extern osd_video_config video_config; -#endif /* __OSDWINDOW__ */ +#endif // MAME_OSD_MODULES_OSDWINDOW_H diff --git a/src/osd/modules/output/console.cpp b/src/osd/modules/output/console.cpp index 993f9429e41..14e0f0fdebf 100644 --- a/src/osd/modules/output/console.cpp +++ b/src/osd/modules/output/console.cpp @@ -9,20 +9,23 @@ *******************************************************************c********/ #include "output_module.h" -#include "modules/osdmodule.h" +#include "modules/osdmodule.h" #include "osdcore.h" +namespace osd { + +namespace { + class output_console : public osd_module, public output_module { public: - output_console() - : osd_module(OSD_OUTPUT_PROVIDER, "console"), output_module() + output_console() : osd_module(OSD_OUTPUT_PROVIDER, "console") { } virtual ~output_console() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual void exit() override { } // output_module @@ -31,4 +34,9 @@ public: }; -MODULE_DEFINITION(OUTPUT_CONSOLE, output_console) +} // anonymous namespace + +} // namespace osd + + +MODULE_DEFINITION(OUTPUT_CONSOLE, osd::output_console) diff --git a/src/osd/modules/output/network.cpp b/src/osd/modules/output/network.cpp index 4fc4b382da6..26ce3619a4c 100644 --- a/src/osd/modules/output/network.cpp +++ b/src/osd/modules/output/network.cpp @@ -9,8 +9,9 @@ ***************************************************************************/ #include "output_module.h" -#include "modules/osdmodule.h" + #include "modules/lib/osdobj_common.h" +#include "modules/osdmodule.h" #include "emu.h" @@ -19,181 +20,182 @@ #define ASIO_HAS_EPOLL #endif -#include <thread> -#include <set> #include "asio.h" +#include <memory> +#include <set> +#include <thread> + + +namespace osd { + +namespace { + class output_client { public: - virtual ~output_client() {} - virtual void deliver(std::string &msg) = 0; + virtual ~output_client() { } + virtual void deliver(std::string &msg) = 0; }; using output_client_ptr = std::shared_ptr<output_client>; using client_set = std::set<output_client_ptr>; -class output_session : public output_client, - public std::enable_shared_from_this<output_session> +class output_session : public output_client, public std::enable_shared_from_this<output_session> { public: - output_session(asio::ip::tcp::socket socket, client_set *clients) : - m_socket(std::move(socket)), - m_clients(clients) - { - } - - void start(running_machine &machine) - { - m_machine = &machine; - m_clients->insert(shared_from_this()); - // now send "mame_start = rom" to the newly connected client - std::snprintf(m_data, max_length, "mame_start = %s\r", machine.system().name); - do_write(std::strlen(m_data)); - do_read(); - } - -private: - void deliver(std::string &msg) - { - std::strncpy(m_data, msg.c_str(), max_length); - do_write(msg.size()); - } + output_session(asio::ip::tcp::socket socket, client_set *clients) : + m_socket(std::move(socket)), + m_clients(clients) + { + } - void handle_message(char *msg) - { - const char *equals_delimiter = " = "; - char *msg_name = strtok(msg, equals_delimiter); - char *msg_value = strtok(NULL, equals_delimiter); + void start(running_machine &machine) + { + m_machine = &machine; + m_clients->insert(shared_from_this()); + // now send "mame_start = rom" to the newly connected client + std::snprintf(m_data, max_length, "mame_start = %s\r", machine.system().name); + do_write(std::strlen(m_data)); + do_read(); + } - //printf("handle_message: msg_name [%s] msg_value [%s]\n", msg_name, msg_value); +private: + void deliver(std::string &msg) + { + std::strncpy(m_data, msg.c_str(), max_length); + do_write(msg.size()); + } - if (std::strcmp(msg_name, "mame_message") == 0) + void handle_message(char *msg) { - const char *comma_delimiter = ","; - msg_name = strtok(msg_value, comma_delimiter); - msg_value = strtok(NULL, comma_delimiter); - int id = atoi(msg_name); - int value = atoi(msg_value); + const char *equals_delimiter = " = "; + char *msg_name = strtok(msg, equals_delimiter); + char *msg_value = strtok(NULL, equals_delimiter); - switch(id) + //printf("handle_message: msg_name [%s] msg_value [%s]\n", msg_name, msg_value); + + if (std::strcmp(msg_name, "mame_message") == 0) { - case IM_MAME_PAUSE: - if (value == 1 && !machine().paused()) - { - machine().pause(); - } - else if (value == 0 && machine().paused()) - { - machine().resume(); - } - break; - case IM_MAME_SAVESTATE: - if (value == 0) - { - machine().schedule_load("auto"); - } - else if (value == 1) + const char *comma_delimiter = ","; + msg_name = strtok(msg_value, comma_delimiter); + msg_value = strtok(NULL, comma_delimiter); + int id = atoi(msg_name); + int value = atoi(msg_value); + + switch(id) { - machine().schedule_save("auto"); + case output_module::IM_MAME_PAUSE: + if (value == 1 && !machine().paused()) + machine().pause(); + else if (value == 0 && machine().paused()) + machine().resume(); + break; + case output_module::IM_MAME_SAVESTATE: + if (value == 0) + machine().schedule_load("auto"); + else if (value == 1) + machine().schedule_save("auto"); + break; } - break; } - } -} - - void do_read() - { - auto self(shared_from_this()); - m_socket.async_read_some(asio::buffer(m_input_m_data, max_length), - [this, self](std::error_code ec, std::size_t length) - { - if (!ec) - { - if (length > 0) + } + + void do_read() + { + auto self(shared_from_this()); + m_socket.async_read_some( + asio::buffer(m_input_m_data, max_length), + [this, self] (std::error_code ec, std::size_t length) { - m_input_m_data[length] = '\0'; - handle_message(m_input_m_data); - } - do_read(); - } - else - { - m_clients->erase(shared_from_this()); - } - }); - } - - void do_write(std::size_t length) - { - auto self(shared_from_this()); - asio::async_write(m_socket, asio::buffer(m_data, length), - [this, self](std::error_code ec, std::size_t /*length*/) - { - if (ec) - { - m_clients->erase(shared_from_this()); - } - }); - } - - running_machine &machine() const { return *m_machine; } - - asio::ip::tcp::socket m_socket; - enum { max_length = 1024 }; - char m_data[max_length]; - char m_input_m_data[max_length]; - client_set *m_clients; - running_machine *m_machine; + if (!ec) + { + if (length > 0) + { + m_input_m_data[length] = '\0'; + handle_message(m_input_m_data); + } + do_read(); + } + else + { + m_clients->erase(shared_from_this()); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + asio::async_write( + m_socket, + asio::buffer(m_data, length), + [this, self] (std::error_code ec, std::size_t /*length*/) + { + if (ec) + { + m_clients->erase(shared_from_this()); + } + }); + } + + running_machine &machine() const { return *m_machine; } + + asio::ip::tcp::socket m_socket; + enum { max_length = 1024 }; + char m_data[max_length]; + char m_input_m_data[max_length]; + client_set *m_clients; + running_machine *m_machine; }; class output_network_server { public: - output_network_server(asio::io_context& io_context, short port, running_machine &machine) : - m_acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) - { - m_machine = &machine; - do_accept(); - } - - void deliver_to_all(std::string msg) - { - for (const auto &client: m_clients) - client->deliver(msg); - } + output_network_server(asio::io_context& io_context, short port, running_machine &machine) : + m_acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) + { + m_machine = &machine; + do_accept(); + } + + void deliver_to_all(std::string msg) + { + for (const auto &client: m_clients) + client->deliver(msg); + } private: - void do_accept() - { - m_acceptor.async_accept( - [this](std::error_code ec, asio::ip::tcp::socket socket) - { - if (!ec) - { - std::make_shared<output_session>(std::move(socket),&m_clients)->start(machine()); - } + void do_accept() + { + m_acceptor.async_accept( + [this] (std::error_code ec, asio::ip::tcp::socket socket) + { + if (!ec) + { + std::make_shared<output_session>(std::move(socket),&m_clients)->start(machine()); + } - do_accept(); - }); - } + do_accept(); + }); + } - running_machine &machine() const { return *m_machine; } + running_machine &machine() const { return *m_machine; } - asio::ip::tcp::acceptor m_acceptor; - client_set m_clients; - running_machine *m_machine; + asio::ip::tcp::acceptor m_acceptor; + client_set m_clients; + running_machine *m_machine; }; class output_network : public osd_module, public output_module { public: - output_network() - : osd_module(OSD_OUTPUT_PROVIDER, "network"), - output_module(), - m_io_context(nullptr), m_server(nullptr) + output_network() : + osd_module(OSD_OUTPUT_PROVIDER, "network"), + output_module(), + m_machine(nullptr) { } @@ -201,9 +203,10 @@ public: { } - virtual int init(const osd_options &options) override + virtual int init(osd_interface &osd, const osd_options &options) override { - m_working_thread = std::thread([](output_network* self) { self->process_output(); }, this); + m_machine = &downcast<osd_common_t &>(osd).machine(); + m_working_thread = std::thread([] (output_network* self) { self->process_output(); }, this); return 0; } @@ -213,31 +216,38 @@ public: notify("mame_stop", 1); m_io_context->stop(); m_working_thread.join(); - delete m_server; - delete m_io_context; + m_server.reset(); + m_io_context.reset(); + m_machine = nullptr; } // output_module virtual void notify(const char *outname, int32_t value) override { - static char buf[256]; - sprintf(buf, "%s = %d\r", ((outname==nullptr) ? "none" : outname), value); - m_server->deliver_to_all(buf); + auto msg = util::string_format("%s = %d\r", ((outname==nullptr) ? "none" : outname), value); + m_server->deliver_to_all(msg); } // implementation void process_output() { - m_io_context = new asio::io_context(); - m_server = new output_network_server(*m_io_context, 8000, machine()); + m_io_context.reset(new asio::io_context); + m_server.reset(new output_network_server(*m_io_context, 8000, machine())); m_io_context->run(); } private: + running_machine &machine() { return *m_machine; } + std::thread m_working_thread; - asio::io_context *m_io_context; - output_network_server *m_server; + std::unique_ptr<asio::io_context> m_io_context; + std::unique_ptr<output_network_server> m_server; + running_machine *m_machine; }; -MODULE_DEFINITION(OUTPUT_NETWORK, output_network) +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(OUTPUT_NETWORK, osd::output_network) diff --git a/src/osd/modules/output/none.cpp b/src/osd/modules/output/none.cpp index a4a613ef29f..3e06921827d 100644 --- a/src/osd/modules/output/none.cpp +++ b/src/osd/modules/output/none.cpp @@ -9,18 +9,22 @@ *******************************************************************c********/ #include "output_module.h" + #include "modules/osdmodule.h" +namespace osd { + +namespace { + class output_none : public osd_module, public output_module { public: - output_none() - : osd_module(OSD_OUTPUT_PROVIDER, "none"), output_module() + output_none() : osd_module(OSD_OUTPUT_PROVIDER, "none") { } virtual ~output_none() { } - virtual int init(const osd_options &options) override { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) override { return 0; } virtual void exit() override { } // output_module @@ -29,4 +33,8 @@ public: }; -MODULE_DEFINITION(OUTPUT_NONE, output_none) +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(OUTPUT_NONE, osd::output_none) diff --git a/src/osd/modules/output/output_module.h b/src/osd/modules/output/output_module.h index 36194e356ea..ae86cfe9084 100644 --- a/src/osd/modules/output/output_module.h +++ b/src/osd/modules/output/output_module.h @@ -4,12 +4,15 @@ * outpout_module.h * */ +#ifndef MAME_OSD_OUTPUT_OUTPUT_MODULE_H +#define MAME_OSD_OUTPUT_OUTPUT_MODULE_H -#ifndef OUTPUT_MODULE_H_ -#define OUTPUT_MODULE_H_ +#pragma once #include "osdepend.h" -#include "modules/osdmodule.h" + +#include <cstdint> + //============================================================ // CONSTANTS @@ -17,22 +20,15 @@ #define OSD_OUTPUT_PROVIDER "output" -#define IM_MAME_PAUSE 0 -#define IM_MAME_SAVESTATE 1 - class output_module { public: - output_module(): m_machine(nullptr) { } + static inline constexpr unsigned IM_MAME_PAUSE = 0; + static inline constexpr unsigned IM_MAME_SAVESTATE = 1; - virtual ~output_module() { } + virtual ~output_module() = default; virtual void notify(const char *outname, int32_t value) = 0; - - void set_machine(running_machine *machine) { m_machine = machine; } - running_machine &machine() const { return *m_machine; } -private: - running_machine *m_machine; }; -#endif /* OUTPUT_MODULE_H_ */ +#endif // MAME_OSD_OUTPUT_OUTPUT_MODULE_H diff --git a/src/osd/modules/output/win32_output.cpp b/src/osd/modules/output/win32_output.cpp index a7a36861989..0d2d355b577 100644 --- a/src/osd/modules/output/win32_output.cpp +++ b/src/osd/modules/output/win32_output.cpp @@ -7,21 +7,26 @@ //============================================================ #include "output_module.h" + #include "modules/osdmodule.h" -#include "modules/lib/osdobj_common.h" #if defined(OSD_WINDOWS) -// standard windows headers -#include <windows.h> +#include "win32_output.h" + +#include "winmain.h" +#include "winutil.h" // MAME headers #include "emu.h" -#include "winmain.h" -#include "winutil.h" -#include "win32_output.h" +// standard windows headers +#include <windows.h> + + +namespace osd { +namespace { //============================================================ // CONSTANTS @@ -66,30 +71,36 @@ class output_win32 : public osd_module, public output_module { public: output_win32() - : osd_module(OSD_OUTPUT_PROVIDER, "windows"), output_module(), m_output_hwnd(nullptr), m_clientlist(nullptr) + : osd_module(OSD_OUTPUT_PROVIDER, "windows") + , output_module() + , m_output_hwnd(nullptr) + , m_clientlist(nullptr) + , m_machine(nullptr) { } virtual ~output_win32() { } - virtual int init(const osd_options &options) override; + virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; // output_module - virtual void notify(const char *outname, int32_t value) override; - int create_window_class(); + running_machine &machine() { return *m_machine; } + LRESULT register_client(HWND hwnd, LPARAM id); LRESULT unregister_client(HWND hwnd, LPARAM id); LRESULT send_id_string(HWND hwnd, LPARAM id); private: + int create_window_class(); // our HWND HWND m_output_hwnd; // client list registered_client * m_clientlist; + running_machine * m_machine; }; @@ -97,8 +108,10 @@ private: // output_init //============================================================ -int output_win32::init(const osd_options &options) +int output_win32::init(osd_interface &osd, const osd_options &options) { + m_machine = &downcast<osd_common_t &>(osd).machine(); + int result; // reset globals @@ -226,13 +239,13 @@ static LRESULT CALLBACK output_window_proc(HWND wnd, UINT message, WPARAM wparam { switch(wparam) { - case IM_MAME_PAUSE: + case output_module::IM_MAME_PAUSE: if (lparam == 1 && !output.machine().paused()) output.machine().pause(); else if (lparam == 0 && output.machine().paused()) output.machine().resume(); break; - case IM_MAME_SAVESTATE: + case output_module::IM_MAME_SAVESTATE: if (lparam == 0) output.machine().schedule_load("auto"); else if (lparam == 1) @@ -352,10 +365,15 @@ void output_win32::notify(const char *outname, int32_t value) PostMessage(client->hwnd, om_mame_update_state, client->machine->output().name_to_id(outname), value); } +} // anonymous namespace + +} // namespace osd #else - MODULE_NOT_SUPPORTED(output_win32, OSD_OUTPUT_PROVIDER, "windows") + +namespace osd { namespace { MODULE_NOT_SUPPORTED(output_win32, OSD_OUTPUT_PROVIDER, "windows") } } + #endif -MODULE_DEFINITION(OUTPUT_WIN32, output_win32) +MODULE_DEFINITION(OUTPUT_WIN32, osd::output_win32) diff --git a/src/osd/modules/output/win32_output.h b/src/osd/modules/output/win32_output.h index f0996b52082..bff3219b21f 100644 --- a/src/osd/modules/output/win32_output.h +++ b/src/osd/modules/output/win32_output.h @@ -2,12 +2,14 @@ // copyright-holders:Aaron Giles //============================================================ // -// output.h - Win32 implementation of MAME output routines +// win32_output.h - Win32 implementation of MAME output routines // //============================================================ -#ifndef __WINDOWS_OUTPUT_H__ -#define __WINDOWS_OUTPUT_H__ +#ifndef MAME_OSD_OUTPUT_WIN32_OUTPUT_H +#define MAME_OSD_OUTPUT_WIN32_OUTPUT_H + +#pragma once //============================================================ @@ -83,8 +85,8 @@ struct copydata_id_string { - uint32_t id; // ID that was requested + uint32_t id; // ID that was requested char string[1]; // string array containing the data }; -#endif /* __WINDOWS_OUTPUT_H__ */ +#endif // MAME_OSD_OUTPUT_WIN32_OUTPUT_H diff --git a/src/osd/modules/render/aviwrite.cpp b/src/osd/modules/render/aviwrite.cpp index 9148244111b..e86d8f99ba6 100644 --- a/src/osd/modules/render/aviwrite.cpp +++ b/src/osd/modules/render/aviwrite.cpp @@ -32,7 +32,7 @@ avi_write::~avi_write() } } -void avi_write::record(const char *name) +void avi_write::record(std::string_view name) { if (m_recording) { @@ -48,7 +48,7 @@ void avi_write::stop() end_avi_recording(); } -void avi_write::begin_avi_recording(const char *name) +void avi_write::begin_avi_recording(std::string_view name) { // stop any existing recording end_avi_recording(); @@ -81,7 +81,7 @@ void avi_write::begin_avi_recording(const char *name) // create a new temporary movie file emu_file tempfile(m_machine.options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - const std::error_condition filerr = (!name || !name[0] || !std::strcmp(name, OSDOPTVAL_AUTO)) + const std::error_condition filerr = (name.empty() || name == OSDOPTVAL_AUTO) ? m_machine.video().open_next(tempfile, "avi") : tempfile.open(name); diff --git a/src/osd/modules/render/aviwrite.h b/src/osd/modules/render/aviwrite.h index e59ce275168..a933dc2a3e6 100644 --- a/src/osd/modules/render/aviwrite.h +++ b/src/osd/modules/render/aviwrite.h @@ -6,13 +6,20 @@ // //============================================================ +#ifndef MAME_RENDER_AVIWRITE_H +#define MAME_RENDER_AVIWRITE_H + #pragma once -#ifndef __RENDER_AVIWRITE__ -#define __RENDER_AVIWRITE__ +// emu +#include "attotime.h" +// lib/util #include "aviio.h" +#include <string_view> + + class running_machine; class avi_write @@ -21,7 +28,7 @@ public: avi_write(running_machine& machine, uint32_t width, uint32_t height); ~avi_write(); - void record(const char *name); + void record(std::string_view name); void stop(); void audio_frame(const int16_t *buffer, int samples_this_frame); void video_frame(bitmap_rgb32& snap); @@ -30,7 +37,7 @@ public: bool recording() const { return m_recording; } private: - void begin_avi_recording(const char *name); + void begin_avi_recording(std::string_view name); void end_avi_recording(); running_machine& m_machine; @@ -47,4 +54,4 @@ private: attotime m_next_frame_time; }; -#endif // __RENDER_AVIWRITE__ +#endif // MAME_RENDER_AVIWRITE_H diff --git a/src/osd/modules/render/bgfx/blendreader.cpp b/src/osd/modules/render/bgfx/blendreader.cpp index 9e7ddb17ce3..53cd57a9f4a 100644 --- a/src/osd/modules/render/bgfx/blendreader.cpp +++ b/src/osd/modules/render/bgfx/blendreader.cpp @@ -44,8 +44,11 @@ uint64_t blend_reader::read_from_value(const Value& value) uint64_t equation = get_enum_from_value(value, "equation", BGFX_STATE_BLEND_EQUATION_ADD, EQUATION_NAMES, EQUATION_COUNT); uint64_t srccolor = get_enum_from_value(value, "srcColor", BGFX_STATE_BLEND_ONE, FUNCTION_NAMES, FUNCTION_COUNT); uint64_t dstcolor = get_enum_from_value(value, "dstColor", BGFX_STATE_BLEND_ZERO, FUNCTION_NAMES, FUNCTION_COUNT); - uint64_t srcalpha = get_enum_from_value(value, "srcAlpha", BGFX_STATE_BLEND_ONE, FUNCTION_NAMES, FUNCTION_COUNT); - uint64_t dstalpha = get_enum_from_value(value, "dstAlpha", BGFX_STATE_BLEND_ZERO, FUNCTION_NAMES, FUNCTION_COUNT); - - return BGFX_STATE_BLEND_EQUATION(equation) | BGFX_STATE_BLEND_FUNC_SEPARATE(srccolor, dstcolor, srcalpha, dstalpha); + if (value.HasMember("srcAlpha") && value.HasMember("dstAlpha")) + { + uint64_t srcalpha = get_enum_from_value(value, "srcAlpha", BGFX_STATE_BLEND_ONE, FUNCTION_NAMES, FUNCTION_COUNT); + uint64_t dstalpha = get_enum_from_value(value, "dstAlpha", BGFX_STATE_BLEND_ZERO, FUNCTION_NAMES, FUNCTION_COUNT); + return BGFX_STATE_BLEND_EQUATION(equation) | BGFX_STATE_BLEND_FUNC_SEPARATE(srccolor, dstcolor, srcalpha, dstalpha); + } + return BGFX_STATE_BLEND_EQUATION(equation) | BGFX_STATE_BLEND_FUNC(srccolor, dstcolor); } diff --git a/src/osd/modules/render/bgfx/chain.cpp b/src/osd/modules/render/bgfx/chain.cpp index b677569a67f..8d5adb0ec25 100644 --- a/src/osd/modules/render/bgfx/chain.cpp +++ b/src/osd/modules/render/bgfx/chain.cpp @@ -27,15 +27,24 @@ #include "clear.h" #include "modules/osdwindow.h" -bgfx_chain::bgfx_chain(std::string name, std::string author, bool transform, target_manager& targets, std::vector<bgfx_slider*> sliders, std::vector<bgfx_parameter*> params, std::vector<bgfx_chain_entry*> entries, std::vector<bgfx_target*> target_list, std::uint32_t screen_index) - : m_name(name) - , m_author(author) +bgfx_chain::bgfx_chain( + std::string &&name, + std::string &&author, + bool transform, + target_manager& targets, + std::vector<bgfx_slider*> &&sliders, + std::vector<bgfx_parameter*> &¶ms, + std::vector<bgfx_chain_entry*> &&entries, + std::vector<bgfx_target*> &&target_list, + std::uint32_t screen_index) + : m_name(std::move(name)) + , m_author(std::move(author)) , m_transform(transform) , m_targets(targets) - , m_sliders(sliders) - , m_params(params) - , m_entries(entries) - , m_target_list(target_list) + , m_sliders(std::move(sliders)) + , m_params(std::move(params)) + , m_entries(std::move(entries)) + , m_target_list(std::move(target_list)) , m_current_time(0) , m_screen_index(screen_index) , m_has_converter(false) @@ -78,7 +87,7 @@ void bgfx_chain::repopulate_targets() } } -void bgfx_chain::process(chain_manager::screen_prim &prim, int view, int screen, texture_manager& textures, osd_window& window, uint64_t blend) +void bgfx_chain::process(chain_manager::screen_prim &prim, int view, int screen, texture_manager& textures, osd_window& window) { screen_device_enumerator screen_iterator(window.machine().root_device()); screen_device* screen_device = screen_iterator.byindex(screen); @@ -108,11 +117,12 @@ void bgfx_chain::process(chain_manager::screen_prim &prim, int view, int screen, } int current_view = view; - for (bgfx_chain_entry* entry : m_entries) + for (size_t i = 0; i < m_entries.size(); i++) { - if (!entry->skip()) + if (!m_entries[i]->skip()) { - entry->submit(current_view, prim, textures, screen_count, screen_width, screen_height, screen_scale_x, screen_scale_y, screen_offset_x, screen_offset_y, rotation_type, swap_xy, blend, screen); + m_entries[i]->submit(current_view, prim, textures, screen_count, screen_width, screen_height, screen_scale_x, screen_scale_y, screen_offset_x, screen_offset_y, + rotation_type, swap_xy, screen); current_view++; } } @@ -145,7 +155,7 @@ uint32_t bgfx_chain::applicable_passes() return applicable_passes; } -void bgfx_chain::insert_effect(uint32_t index, bgfx_effect *effect, std::string name, std::string source, chain_manager &chains) +void bgfx_chain::insert_effect(uint32_t index, bgfx_effect *effect, const bool apply_tint, std::string name, std::string source, chain_manager &chains) { auto *clear = new clear_state(BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0, 1.0f, 0); std::vector<bgfx_suppressor*> suppressors; @@ -164,11 +174,15 @@ void bgfx_chain::insert_effect(uint32_t index, bgfx_effect *effect, std::string uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_tex_size1", bgfx::UniformType::Vec4), values, 4)); uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_inv_tex_size0", bgfx::UniformType::Vec4), values, 4)); uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_inv_tex_size1", bgfx::UniformType::Vec4), values, 4)); + uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_tex_bounds0", bgfx::UniformType::Vec4), values, 4)); + uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_tex_bounds1", bgfx::UniformType::Vec4), values, 4)); + uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_inv_tex_bounds0", bgfx::UniformType::Vec4), values, 4)); + uniforms.push_back(new bgfx_value_uniform(new bgfx_uniform("u_inv_tex_bounds1", bgfx::UniformType::Vec4), values, 4)); - m_entries.insert(m_entries.begin() + index, new bgfx_chain_entry(name, effect, clear, suppressors, inputs, uniforms, m_targets, "screen", false)); + m_entries.insert(m_entries.begin() + index, new bgfx_chain_entry(name, effect, clear, suppressors, inputs, uniforms, m_targets, "screen", apply_tint)); const uint32_t screen_width = chains.targets().width(TARGET_STYLE_GUEST, m_screen_index); const uint32_t screen_height = chains.targets().height(TARGET_STYLE_GUEST, m_screen_index); m_targets.destroy_target("screen", m_screen_index); - m_targets.create_target("screen", bgfx::TextureFormat::BGRA8, screen_width, screen_height, TARGET_STYLE_GUEST, true, false, 1, m_screen_index); + m_targets.create_target("screen", bgfx::TextureFormat::BGRA8, screen_width, screen_height, 1, 1, TARGET_STYLE_GUEST, true, false, 1, m_screen_index); } diff --git a/src/osd/modules/render/bgfx/chain.h b/src/osd/modules/render/bgfx/chain.h index 5080532210c..5d84bb1ed0c 100644 --- a/src/osd/modules/render/bgfx/chain.h +++ b/src/osd/modules/render/bgfx/chain.h @@ -6,17 +6,17 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_CHAIN_H +#define MAME_RENDER_BGFX_CHAIN_H + #pragma once -#ifndef __DRAWBGFX_CHAIN__ -#define __DRAWBGFX_CHAIN__ +#include "chainentry.h" #include <string> #include <vector> #include <map> -#include "chainentry.h" - class bgfx_slider; class bgfx_parameter; class texture_manager; @@ -27,13 +27,14 @@ class osd_window; class bgfx_chain { public: - bgfx_chain(std::string name, std::string author, bool transform, target_manager& targets, std::vector<bgfx_slider*> sliders, std::vector<bgfx_parameter*> params, std::vector<bgfx_chain_entry*> entries, std::vector<bgfx_target*> target_list, uint32_t screen_index); + bgfx_chain(std::string &&name, std::string &&author, bool transform, target_manager& targets, std::vector<bgfx_slider*> &&sliders, std::vector<bgfx_parameter*> &¶ms, std::vector<bgfx_chain_entry*> &&entries, std::vector<bgfx_target*> &&target_list, uint32_t screen_index); ~bgfx_chain(); - void process(chain_manager::screen_prim &prim, int view, int screen, texture_manager& textures, osd_window &window, uint64_t blend = 0L); + void process(chain_manager::screen_prim &prim, int view, int screen, texture_manager& textures, osd_window &window); void repopulate_targets(); // Getters + const std::string &name() const { return m_name; } std::vector<bgfx_slider*>& sliders() { return m_sliders; } std::vector<bgfx_chain_entry*>& entries() { return m_entries; } uint32_t applicable_passes(); @@ -44,7 +45,7 @@ public: // Setters void set_has_converter(bool has_converter) { m_has_converter = has_converter; } void set_has_adjuster(bool has_adjuster) { m_has_adjuster = has_adjuster; } - void insert_effect(uint32_t index, bgfx_effect *effect, std::string name, std::string source, chain_manager &chains); + void insert_effect(uint32_t index, bgfx_effect *effect, const bool apply_tint, std::string name, std::string source, chain_manager &chains); private: std::string m_name; @@ -63,4 +64,4 @@ private: bool m_has_adjuster; }; -#endif // __DRAWBGFX_CHAIN__ +#endif // MAME_RENDER_BGFX_CHAIN_H diff --git a/src/osd/modules/render/bgfx/chainentry.cpp b/src/osd/modules/render/bgfx/chainentry.cpp index 45c0b805882..c3f3ee3a8e3 100644 --- a/src/osd/modules/render/bgfx/chainentry.cpp +++ b/src/osd/modules/render/bgfx/chainentry.cpp @@ -30,7 +30,8 @@ #include "render.h" -bgfx_chain_entry::bgfx_chain_entry(std::string name, bgfx_effect* effect, clear_state* clear, std::vector<bgfx_suppressor*> suppressors, std::vector<bgfx_input_pair*> inputs, std::vector<bgfx_entry_uniform*> uniforms, target_manager& targets, std::string output, bool apply_tint) +bgfx_chain_entry::bgfx_chain_entry(std::string name, bgfx_effect* effect, clear_state* clear, std::vector<bgfx_suppressor*> suppressors, std::vector<bgfx_input_pair*> inputs, + std::vector<bgfx_entry_uniform*> uniforms, target_manager& targets, std::string output, bool apply_tint) : m_name(name) , m_effect(effect) , m_clear(clear) @@ -55,12 +56,18 @@ bgfx_chain_entry::~bgfx_chain_entry() delete uniform; } m_uniforms.clear(); - delete m_clear; + if (m_clear) + { + delete m_clear; + } } -void bgfx_chain_entry::submit(int view, chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, uint64_t blend, int32_t screen) +void bgfx_chain_entry::submit(int view, chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, + float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, int32_t screen) { - if (!setup_view(textures, view, screen_width, screen_height, screen)) + uint16_t view_width = screen_width; + uint16_t view_height = screen_height; + if (!setup_view(textures, view, screen_width, screen_height, screen, view_width, view_height)) { return; } @@ -73,18 +80,19 @@ void bgfx_chain_entry::submit(int view, chain_manager::screen_prim &prim, textur uint32_t tint = 0xffffffff; if (m_apply_tint) { - const auto a = (uint8_t)std::round(prim.m_prim->color.a * 255); - const auto r = (uint8_t)std::round(prim.m_prim->color.r * 255); - const auto g = (uint8_t)std::round(prim.m_prim->color.g * 255); - const auto b = (uint8_t)std::round(prim.m_prim->color.b * 255); + const uint8_t a = (uint8_t)std::round(prim.m_prim->color.a * 255); + const uint8_t r = (uint8_t)std::round(prim.m_prim->color.r * 255); + const uint8_t g = (uint8_t)std::round(prim.m_prim->color.g * 255); + const uint8_t b = (uint8_t)std::round(prim.m_prim->color.b * 255); tint = (a << 24) | (b << 16) | (g << 8) | r; } bgfx::TransientVertexBuffer buffer; - put_screen_buffer(prim.m_screen_width, prim.m_screen_height, tint, &buffer); + put_screen_buffer(view_width, view_height, tint, &buffer); bgfx::setVertexBuffer(0, &buffer); - setup_auto_uniforms(prim, textures, screen_count, screen_width, screen_height, screen_scale_x, screen_scale_y, screen_offset_x, screen_offset_y, rotation_type, swap_xy, screen); + setup_auto_uniforms(prim, textures, screen_count, view_width, view_height, screen_width, screen_height, screen_scale_x, screen_scale_y, screen_offset_x, screen_offset_y, + rotation_type, swap_xy, screen); for (bgfx_entry_uniform* uniform : m_uniforms) { @@ -94,7 +102,7 @@ void bgfx_chain_entry::submit(int view, chain_manager::screen_prim &prim, textur } } - m_effect->submit(view, blend); + m_effect->submit(view); if (m_targets.target(screen, m_output) != nullptr) { @@ -102,6 +110,43 @@ void bgfx_chain_entry::submit(int view, chain_manager::screen_prim &prim, textur } } +void bgfx_chain_entry::setup_auto_uniforms(chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t view_width, uint16_t view_height, + uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, + uint32_t rotation_type, bool swap_xy, int32_t screen) +{ + setup_viewsize_uniforms(view_width, view_height); + setup_screensize_uniforms(textures, screen_width, screen_height, screen); + setup_screenscale_uniforms(screen_scale_x, screen_scale_y); + setup_screenoffset_uniforms(screen_offset_x, screen_offset_y); + setup_screencount_uniforms(screen_count); + setup_sourcesize_uniform(prim); + setup_targetsize_uniform(screen); + setup_targetscale_uniform(screen); + setup_rotationtype_uniform(rotation_type); + setup_swapxy_uniform(swap_xy); + setup_quaddims_uniform(prim); + setup_screenindex_uniform(screen); +} + +void bgfx_chain_entry::setup_viewsize_uniforms(uint16_t view_width, uint16_t view_height) +{ + float width(view_width); + float height(view_height); + bgfx_uniform* view_dims = m_effect->uniform("u_view_dims"); + if (view_dims != nullptr) + { + float values[2] = { width, height }; + view_dims->set(values, sizeof(float) * 2); + } + + bgfx_uniform* inv_view_dims = m_effect->uniform("u_inv_view_dims"); + if (inv_view_dims != nullptr) + { + float values[2] = { -1.0f / width, 1.0f / height }; + inv_view_dims->set(values, sizeof(float) * 2); + } +} + void bgfx_chain_entry::setup_screensize_uniforms(texture_manager& textures, uint16_t screen_width, uint16_t screen_height, int32_t screen) { float width = screen_width; @@ -235,22 +280,8 @@ void bgfx_chain_entry::setup_screenindex_uniform(int32_t screen) const } } -void bgfx_chain_entry::setup_auto_uniforms(chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, int32_t screen) -{ - setup_screensize_uniforms(textures, screen_width, screen_height, screen); - setup_screenscale_uniforms(screen_scale_x, screen_scale_y); - setup_screenoffset_uniforms(screen_offset_x, screen_offset_y); - setup_screencount_uniforms(screen_count); - setup_sourcesize_uniform(prim); - setup_targetsize_uniform(screen); - setup_targetscale_uniform(screen); - setup_rotationtype_uniform(rotation_type); - setup_swapxy_uniform(swap_xy); - setup_quaddims_uniform(prim); - setup_screenindex_uniform(screen); -} - -bool bgfx_chain_entry::setup_view(texture_manager &textures, int view, uint16_t screen_width, uint16_t screen_height, int32_t screen) const +bool bgfx_chain_entry::setup_view(texture_manager &textures, int view, uint16_t screen_width, uint16_t screen_height, int32_t screen, + uint16_t &out_view_width, uint16_t &out_view_height) const { bgfx::FrameBufferHandle handle = BGFX_INVALID_HANDLE; uint16_t width = screen_width; @@ -269,11 +300,13 @@ bool bgfx_chain_entry::setup_view(texture_manager &textures, int view, uint16_t bgfx::setViewFrameBuffer(view, handle); bgfx::setViewRect(view, 0, 0, width, height); + out_view_width = width; + out_view_height = height; const bgfx::Caps* caps = bgfx::getCaps(); std::string name = m_inputs[0]->texture() + std::to_string(screen); - const float right_ratio = (float)textures.provider(name)->width() / textures.provider(name)->rowpixels(); + const float right_ratio = float(textures.provider(name)->width()) / textures.provider(name)->rowpixels(); float projMat[16]; bx::mtxOrtho(projMat, 0.0f, right_ratio, 1.0f, 0.0f, 0.0f, 100.0f, 0.0f, caps->homogeneousDepth); @@ -307,14 +340,6 @@ void bgfx_chain_entry::put_screen_buffer(uint16_t screen_width, uint16_t screen_ v[0] = v[1] = 1; v[2] = v[3] = 0; } - else if (renderer_type == bgfx::RendererType::Direct3D9) - { - for (int i = 0; i < 4; i++) - { - u[i] += 0.5f / screen_width; - v[i] += 0.5f / screen_height; - } - } vertex[0].m_x = x[0]; vertex[0].m_y = y[0]; diff --git a/src/osd/modules/render/bgfx/chainentry.h b/src/osd/modules/render/bgfx/chainentry.h index 3fb1dd54ca6..a7504187879 100644 --- a/src/osd/modules/render/bgfx/chainentry.h +++ b/src/osd/modules/render/bgfx/chainentry.h @@ -34,10 +34,12 @@ class target_manager; class bgfx_chain_entry { public: - bgfx_chain_entry(std::string name, bgfx_effect* effect, clear_state* clear, std::vector<bgfx_suppressor*> suppressors, std::vector<bgfx_input_pair*> inputs, std::vector<bgfx_entry_uniform*> uniforms, target_manager& targets, std::string output, bool apply_tint); + bgfx_chain_entry(std::string name, bgfx_effect* effect, clear_state* clear, std::vector<bgfx_suppressor*> suppressors, std::vector<bgfx_input_pair*> inputs, + std::vector<bgfx_entry_uniform*> uniforms, target_manager& targets, std::string output, bool apply_tint); ~bgfx_chain_entry(); - void submit(int view, chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, uint64_t blend, int32_t screen); + void submit(int view, chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, + float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, int32_t screen); // Getters std::string name() const { return m_name; } @@ -45,7 +47,10 @@ public: bool skip(); private: - void setup_auto_uniforms(chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, uint32_t rotation_type, bool swap_xy, int32_t screen); + void setup_auto_uniforms(chain_manager::screen_prim &prim, texture_manager& textures, uint16_t screen_count, uint16_t view_width, uint16_t view_height, + uint16_t screen_width, uint16_t screen_height, float screen_scale_x, float screen_scale_y, float screen_offset_x, float screen_offset_y, + uint32_t rotation_type, bool swap_xy, int32_t screen); + void setup_viewsize_uniforms(uint16_t view_width, uint16_t view_height); void setup_screensize_uniforms(texture_manager& textures, uint16_t screen_width, uint16_t screen_height, int32_t screen); void setup_screenscale_uniforms(float screen_scale_x, float screen_scale_y); void setup_screenoffset_uniforms(float screen_offset_x, float screen_offset_y); @@ -58,7 +63,8 @@ private: void setup_quaddims_uniform(chain_manager::screen_prim &prim) const; void setup_screenindex_uniform(int32_t screen) const; - bool setup_view(texture_manager& textures, int view, uint16_t screen_width, uint16_t screen_height, int32_t screen) const; + bool setup_view(texture_manager& textures, int view, uint16_t screen_width, uint16_t screen_height, int32_t screen, + uint16_t &out_view_width, uint16_t &out_view_height) const; void put_screen_buffer(uint16_t screen_width, uint16_t screen_height, uint32_t screen_tint, bgfx::TransientVertexBuffer* buffer) const; std::string m_name; diff --git a/src/osd/modules/render/bgfx/chainentryreader.cpp b/src/osd/modules/render/bgfx/chainentryreader.cpp index c6b9953603f..ce58816e827 100644 --- a/src/osd/modules/render/bgfx/chainentryreader.cpp +++ b/src/osd/modules/render/bgfx/chainentryreader.cpp @@ -29,7 +29,13 @@ #include "clear.h" #include "clearreader.h" -bgfx_chain_entry* chain_entry_reader::read_from_value(const Value& value, std::string prefix, chain_manager& chains, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params, uint32_t screen_index) +bgfx_chain_entry* chain_entry_reader::read_from_value( + const Value &value, + const std::string &prefix, + chain_manager &chains, + std::map<std::string, bgfx_slider*> &sliders, + std::map<std::string, bgfx_parameter*> ¶ms, + uint32_t screen_index) { if (!validate_parameters(value, prefix)) { @@ -52,18 +58,18 @@ bgfx_chain_entry* chain_entry_reader::read_from_value(const Value& value, std::s for (uint32_t i = 0; i < input_array.Size(); i++) { const Value& input = input_array[i]; - if (!READER_CHECK(input.HasMember("sampler"), (prefix + "input[" + std::to_string(i) + ": Must have string value 'sampler' (what sampler are we binding to?)\n").c_str())) return nullptr; - if (!READER_CHECK(input["sampler"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'sampler' must be a string\n").c_str())) return nullptr; + if (!READER_CHECK(input.HasMember("sampler"), "%sinput[%u]: Must have string value 'sampler' (what sampler are we binding to?)\n", prefix, i)) return nullptr; + if (!READER_CHECK(input["sampler"].IsString(), "%sinput[%u]: Value 'sampler' must be a string\n", prefix, i)) return nullptr; bool has_texture = input.HasMember("texture"); bool has_target = input.HasMember("target"); bool has_option = input.HasMember("option"); - if (!READER_CHECK(has_texture || has_target || has_option, (prefix + "input[" + std::to_string(i) + ": Must have string value 'target', 'texture' or 'option' (what source are we using?)\n").c_str())) return nullptr; - if (!READER_CHECK(!has_texture || input["texture"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'texture' must be a string\n").c_str())) return nullptr; - if (!READER_CHECK(!has_target || input["target"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'target' must be a string\n").c_str())) return nullptr; - if (!READER_CHECK(!has_option || input["option"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'option' must be a string\n").c_str())) return nullptr; - if (!READER_CHECK(has_target || !input.HasMember("bilinear") || input["bilinear"].IsBool(), (prefix + "input[" + std::to_string(i) + ": Value 'bilinear' must be a boolean\n").c_str())) return nullptr; - if (!READER_CHECK(has_target || !input.HasMember("clamp") || input["clamp"].IsBool(), (prefix + "input[" + std::to_string(i) + ": Value 'clamp' must be a boolean\n").c_str())) return nullptr; - if (!READER_CHECK(has_texture || has_option || !input.HasMember("selection") || input["selection"].IsString(), (prefix + "input[" + std::to_string(i) + ": Value 'selection' must be a string\n").c_str())) return nullptr; + if (!READER_CHECK(has_texture || has_target || has_option, "%sinput[%u]: Must have string value 'target', 'texture' or 'option' (what source are we using?)\n", prefix, i)) return nullptr; + if (!READER_CHECK(!has_texture || input["texture"].IsString(), "%sinput[%u]: Value 'texture' must be a string\n", prefix, i)) return nullptr; + if (!READER_CHECK(!has_target || input["target"].IsString(), "%sinput[%u]: Value 'target' must be a string\n", prefix, i)) return nullptr; + if (!READER_CHECK(!has_option || input["option"].IsString(), "%sinput[%u]: Value 'option' must be a string\n", prefix, i)) return nullptr; + if (!READER_CHECK(has_target || !input.HasMember("bilinear") || input["bilinear"].IsBool(), "%sinput[%u]: Value 'bilinear' must be a boolean\n", prefix, i)) return nullptr; + if (!READER_CHECK(has_target || !input.HasMember("clamp") || input["clamp"].IsBool(), "%sinput[%u]: Value 'clamp' must be a boolean\n", prefix, i)) return nullptr; + if (!READER_CHECK(has_texture || has_option || !input.HasMember("selection") || input["selection"].IsString(), "%sinput[%u]: Value 'selection' must be a string\n", prefix, i)) return nullptr; bool bilinear = get_bool(input, "bilinear", true); bool clamp = get_bool(input, "clamp", false); std::string selection = get_string(input, "selection", ""); @@ -223,17 +229,17 @@ bgfx_chain_entry* chain_entry_reader::read_from_value(const Value& value, std::s return new bgfx_chain_entry(name, effect, clear, suppressors, inputs, uniforms, chains.targets(), output, applytint); } -bool chain_entry_reader::validate_parameters(const Value& value, std::string prefix) +bool chain_entry_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("effect"), (prefix + "Must have string value 'effect' (what effect does this entry use?)\n").c_str())) return false; - if (!READER_CHECK(value["effect"].IsString(), (prefix + "Value 'effect' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'effect' (what effect does this entry use?)\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("output"), (prefix + "Must have string value 'offset' (what target are we rendering to?)\n").c_str())) return false; - if (!READER_CHECK(value["output"].IsString(), (prefix + "Value 'output' must be a string\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("input") || value["input"].IsArray(), (prefix + "Value 'input' must be an array\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("uniforms") || value["uniforms"].IsArray(), (prefix + "Value 'uniforms' must be an array\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("disablewhen") || value["disablewhen"].IsArray(), (prefix + "Value 'disablewhen' must be an array\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("applytint") || value["applytint"].IsBool(), (prefix + "Value 'applytint' must be a bool\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("effect"), "%sMust have string value 'effect' (what effect does this entry use?)\n", prefix)) return false; + if (!READER_CHECK(value["effect"].IsString(), "%sValue 'effect' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("name"), "%sMust have string value 'effect' (what effect does this entry use?)\n", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%sValue 'name' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("output"), "%sMust have string value 'offset' (what target are we rendering to?)\n", prefix)) return false; + if (!READER_CHECK(value["output"].IsString(), "%sValue 'output' must be a string\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("input") || value["input"].IsArray(), "%sValue 'input' must be an array\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("uniforms") || value["uniforms"].IsArray(), "%sValue 'uniforms' must be an array\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("disablewhen") || value["disablewhen"].IsArray(), "%sValue 'disablewhen' must be an array\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("applytint") || value["applytint"].IsBool(), "%sValue 'applytint' must be a bool\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/chainentryreader.h b/src/osd/modules/render/bgfx/chainentryreader.h index 0f604e9a71f..583e972732c 100644 --- a/src/osd/modules/render/bgfx/chainentryreader.h +++ b/src/osd/modules/render/bgfx/chainentryreader.h @@ -5,15 +5,15 @@ // chainentryreader.h - BGFX chain entry JSON reader // //============================================================ +#ifndef MAME_RENDER_BGFX_CHAINENTRYREADER_H +#define MAME_RENDER_BGFX_CHAINENTRYREADER_H #pragma once -#ifndef __DRAWBGFX_CHAIN_ENTRY_READER__ -#define __DRAWBGFX_CHAIN_ENTRY_READER__ +#include "statereader.h" #include <map> -#include "statereader.h" class bgfx_chain_entry; class bgfx_slider; @@ -23,10 +23,10 @@ class chain_manager; class chain_entry_reader : public state_reader { public: - static bgfx_chain_entry* read_from_value(const Value& value, std::string prefix, chain_manager& chains, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params, uint32_t screen_index); + static bgfx_chain_entry* read_from_value(const Value& value, const std::string &prefix, chain_manager& chains, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params, uint32_t screen_index); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_CHAIN_ENTRY_READER__ +#endif // MAME_RENDER_BGFX_CHAINENTRYREADER_H diff --git a/src/osd/modules/render/bgfx/chainmanager.cpp b/src/osd/modules/render/bgfx/chainmanager.cpp index c3b80e88d14..a15f2f1e584 100644 --- a/src/osd/modules/render/bgfx/chainmanager.cpp +++ b/src/osd/modules/render/bgfx/chainmanager.cpp @@ -26,45 +26,62 @@ #include "bgfxutil.h" -#include "chainreader.h" #include "chain.h" - -#include "texture.h" -#include "target.h" +#include "chainreader.h" #include "slider.h" +#include "target.h" +#include "texture.h" #include "sliderdirtynotifier.h" +#include "util/path.h" +#include "util/unicode.h" +#include "util/xmlfile.h" + #include "osdcore.h" #include "osdfile.h" -using namespace rapidjson; +#include <algorithm> +#include <locale> + -const uint32_t chain_manager::CHAIN_NONE = 0; +using namespace rapidjson; chain_manager::screen_prim::screen_prim(render_primitive *prim) { m_prim = prim; - m_screen_width = (uint16_t)floorf(prim->get_full_quad_width() + 0.5f); - m_screen_height = (uint16_t)floorf(prim->get_full_quad_height() + 0.5f); - m_quad_width = (uint16_t)floorf(prim->get_quad_width() + 0.5f); - m_quad_height = (uint16_t)floorf(prim->get_quad_height() + 0.5f); - m_tex_width = (float)prim->texture.width; - m_tex_height = (float)prim->texture.height; + m_screen_width = uint16_t(floorf(prim->get_full_quad_width() + 0.5f)); + m_screen_height = uint16_t(floorf(prim->get_full_quad_height() + 0.5f)); + m_quad_width = uint16_t(floorf(prim->get_quad_width() + 0.5f)); + m_quad_height = uint16_t(floorf(prim->get_quad_height() + 0.5f)); + m_tex_width = prim->texture.width; + m_tex_height = prim->texture.height; m_rowpixels = prim->texture.rowpixels; m_palette_length = prim->texture.palette_length; m_flags = prim->flags; } -chain_manager::chain_manager(running_machine& machine, osd_options& options, texture_manager& textures, target_manager& targets, effect_manager& effects, uint32_t window_index, slider_dirty_notifier& slider_notifier) +chain_manager::chain_manager( + running_machine& machine, + const osd_options& options, + texture_manager& textures, + target_manager& targets, + effect_manager& effects, + uint32_t window_index, + slider_dirty_notifier& slider_notifier, + uint16_t user_prescale, + uint16_t max_prescale_size) : m_machine(machine) , m_options(options) , m_textures(textures) , m_targets(targets) , m_effects(effects) , m_window_index(window_index) + , m_user_prescale(user_prescale) + , m_max_prescale_size(max_prescale_size) , m_slider_notifier(slider_notifier) , m_screen_count(0) + , m_default_chain_index(-1) { m_converters.clear(); refresh_available_chains(); @@ -87,13 +104,55 @@ void chain_manager::init_texture_converters() m_adjuster = m_effects.get_or_load_effect(m_options, "misc/bcg_adjust"); } +void chain_manager::get_default_chain_info(std::string &out_chain_name, int32_t &out_chain_index) +{ + if (m_default_chain_index == -1) + { + out_chain_index = CHAIN_NONE; + out_chain_name = ""; + return; + } + + out_chain_index = m_default_chain_index; + out_chain_name = "default"; + return; +} + void chain_manager::refresh_available_chains() { m_available_chains.clear(); - m_available_chains.push_back(chain_desc("none", "")); - - const std::string chains_path = osd_subst_env(util::string_format("%s" PATH_SEPARATOR "chains", m_options.bgfx_path())); - find_available_chains(chains_path, ""); + m_available_chains.emplace_back("none", ""); + + find_available_chains(util::path_concat(m_options.bgfx_path(), "chains"), ""); + std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(std::locale()); + std::sort( + m_available_chains.begin(), + m_available_chains.end(), + [&coll] (chain_desc const &x, chain_desc const &y) -> bool + { + if (x.m_name == "none") + return y.m_name != "none"; + else if (y.m_name == "none") + return false; + else if (x.m_name == "default") + return y.m_name != "default"; + else if (y.m_name == "default") + return false; + std::wstring const xstr = wstring_from_utf8(x.m_name); + std::wstring const ystr = wstring_from_utf8(y.m_name); + return coll.compare(xstr.data(), xstr.data() + xstr.size(), ystr.data(), ystr.data() + ystr.size()) < 0; + }); + + if (m_default_chain_index == -1) + { + for (size_t i = 0; i < m_available_chains.size(); i++) + { + if (m_available_chains[i].m_name == "default") + { + m_default_chain_index = int32_t(i); + } + } + } destroy_unloaded_chains(); } @@ -103,7 +162,7 @@ void chain_manager::destroy_unloaded_chains() // O(shaders*available_chains), but we don't care because asset reloading happens rarely for (int i = 0; i < m_chain_names.size(); i++) { - std::string name = m_chain_names[i]; + const std::string &name = m_chain_names[i]; if (name.length() > 0) { for (chain_desc desc : m_available_chains) @@ -112,8 +171,7 @@ void chain_manager::destroy_unloaded_chains() { delete m_screen_chains[i]; m_screen_chains[i] = nullptr; - m_chain_names[i] = ""; - m_current_chain[i] = CHAIN_NONE; + get_default_chain_info(m_chain_names[i], m_current_chain[i]); break; } } @@ -121,56 +179,53 @@ void chain_manager::destroy_unloaded_chains() } } -void chain_manager::find_available_chains(std::string root, std::string path) +void chain_manager::find_available_chains(std::string_view root, std::string_view path) { - osd::directory::ptr directory = osd::directory::open(root + path); - if (directory != nullptr) + osd::directory::ptr directory = osd::directory::open(path.empty() ? std::string(root) : util::path_concat(root, path)); + if (directory) { - for (const osd::directory::entry *entry = directory->read(); entry != nullptr; entry = directory->read()) + for (const osd::directory::entry *entry = directory->read(); entry; entry = directory->read()) { if (entry->type == osd::directory::entry::entry_type::FILE) { - std::string name(entry->name); - std::string extension(".json"); + const std::string_view name(entry->name); + const std::string_view extension(".json"); // Does the name has at least one character in addition to ".json"? if (name.length() > extension.length()) { size_t start = name.length() - extension.length(); - std::string test_segment = name.substr(start, extension.length()); + const std::string_view test_segment = name.substr(start, extension.length()); // Does it end in .json? if (test_segment == extension) { - m_available_chains.push_back(chain_desc(name.substr(0, start), path)); + m_available_chains.emplace_back(std::string(name.substr(0, start)), std::string(path)); } } } else if (entry->type == osd::directory::entry::entry_type::DIR) { - std::string name = entry->name; - if (!(name == "." || name == "..")) + const std::string_view name = entry->name; + if ((name != ".") && (name != "..")) { - std::string appended_path = path + "/" + name; - if (path.length() == 0) - { - appended_path = name; - } - find_available_chains(root, path + "/" + name); + if (path.empty()) + find_available_chains(root, name); + else + find_available_chains(root, util::path_concat(path, name)); } } } } } -bgfx_chain* chain_manager::load_chain(std::string name, uint32_t screen_index) +std::unique_ptr<bgfx_chain> chain_manager::load_chain(std::string name, uint32_t screen_index) { if (name.length() < 5 || (name.compare(name.length() - 5, 5, ".json") != 0)) { - name = name + ".json"; + name += ".json"; } - std::string path = osd_subst_env(util::string_format("%s" PATH_SEPARATOR "chains" PATH_SEPARATOR, m_options.bgfx_path())); - path += name; + const std::string path = util::path_concat(m_options.bgfx_path(), "chains", name); bx::FileReader reader; if (!bx::open(&reader, path.c_str())) @@ -179,29 +234,35 @@ bgfx_chain* chain_manager::load_chain(std::string name, uint32_t screen_index) return nullptr; } - int32_t size(bx::getSize(&reader)); + const int32_t size(bx::getSize(&reader)); - char* data = new char[size + 1]; - bx::read(&reader, reinterpret_cast<void*>(data), size); + bx::ErrorAssert err; + std::unique_ptr<char []> data(new (std::nothrow) char [size + 1]); + if (!data) + { + osd_printf_error("Out of memory reading chain file %s\n", path); + bx::close(&reader); + return nullptr; + } + + bx::read(&reader, reinterpret_cast<void*>(data.get()), size, &err); bx::close(&reader); data[size] = 0; Document document; - document.Parse<kParseCommentsFlag>(data); - - delete [] data; + document.Parse<kParseCommentsFlag>(data.get()); + data.reset(); if (document.HasParseError()) { std::string error(GetParseError_En(document.GetParseError())); - osd_printf_warning("Unable to parse chain %s. Errors returned:\n", path); - osd_printf_warning("%s\n", error); + osd_printf_warning("Unable to parse chain %s. Errors returned:\n%s\n", path, error); return nullptr; } - bgfx_chain* chain = chain_reader::read_from_value(document, name + ": ", *this, screen_index); + std::unique_ptr<bgfx_chain> chain = chain_reader::read_from_value(document, name + ": ", *this, screen_index, m_user_prescale, m_max_prescale_size); - if (chain == nullptr) + if (!chain) { osd_printf_warning("Unable to load chain %s, falling back to no post processing\n", path); return nullptr; @@ -210,17 +271,17 @@ bgfx_chain* chain_manager::load_chain(std::string name, uint32_t screen_index) return chain; } -void chain_manager::parse_chain_selections(std::string chain_str) +void chain_manager::parse_chain_selections(std::string_view chain_str) { - std::vector<std::string> chain_names = split_option_string(chain_str); + std::vector<std::string_view> chain_names = split_option_string(chain_str); if (chain_names.empty()) chain_names.push_back("default"); - while (m_current_chain.size() != chain_names.size()) + while (m_current_chain.size() < chain_names.size()) { - m_screen_chains.push_back(nullptr); - m_chain_names.push_back(""); + m_screen_chains.emplace_back(nullptr); + m_chain_names.emplace_back(); m_current_chain.push_back(CHAIN_NONE); } @@ -230,9 +291,7 @@ void chain_manager::parse_chain_selections(std::string chain_str) for (chain_index = 0; chain_index < m_available_chains.size(); chain_index++) { if (m_available_chains[chain_index].m_name == chain_names[index]) - { break; - } } if (chain_index < m_available_chains.size()) @@ -248,24 +307,35 @@ void chain_manager::parse_chain_selections(std::string chain_str) } } -std::vector<std::string> chain_manager::split_option_string(std::string chain_str) const +std::vector<std::string_view> chain_manager::split_option_string(std::string_view chain_str) const { - std::vector<std::string> chain_names; + std::vector<std::string_view> chain_names; - uint32_t length = chain_str.length(); + const uint32_t length = chain_str.length(); uint32_t win = 0; uint32_t last_start = 0; - for (uint32_t i = 0; i < length + 1; i++) + for (uint32_t i = 0; i <= length; i++) { - if (i == length || chain_str[i] == ',' || chain_str[i] == ':') + if (i == length || (chain_str[i] == ',') || (chain_str[i] == ':')) { - if (win == m_window_index) + if ((win == 0) || (win == m_window_index)) { - chain_names.push_back(chain_str.substr(last_start, i - last_start)); + // treat an empty string as equivalent to "default" + if (i > last_start) + chain_names.push_back(chain_str.substr(last_start, i - last_start)); + else + chain_names.push_back("default"); } + last_start = i + 1; - if (chain_str[i] == ':') + if ((i < length) && (chain_str[i] == ':')) { + // no point walking the rest of the string if this was our window + if (win == m_window_index) + break; + + // don't use first for all if more than one window is specified + chain_names.clear(); win++; } } @@ -282,7 +352,7 @@ void chain_manager::load_chains() { chain_desc& desc = m_available_chains[m_current_chain[chain]]; m_chain_names[chain] = desc.m_name; - m_screen_chains[chain] = load_chain(desc.m_path + "/" + desc.m_name, uint32_t(chain)); + m_screen_chains[chain] = load_chain(util::path_concat(desc.m_path, desc.m_name), uint32_t(chain)).release(); } } } @@ -319,7 +389,7 @@ bgfx_chain* chain_manager::screen_chain(uint32_t screen) void chain_manager::process_screen_quad(uint32_t view, uint32_t screen, screen_prim &prim, osd_window& window) { - const bool any_targets_rebuilt = m_targets.update_target_sizes(screen, prim.m_tex_width, prim.m_tex_height, TARGET_STYLE_GUEST); + const bool any_targets_rebuilt = m_targets.update_target_sizes(screen, prim.m_tex_width, prim.m_tex_height, TARGET_STYLE_GUEST, m_user_prescale, m_max_prescale_size); if (any_targets_rebuilt) { for (bgfx_chain* chain : m_screen_chains) @@ -332,7 +402,7 @@ void chain_manager::process_screen_quad(uint32_t view, uint32_t screen, screen_p } bgfx_chain* chain = screen_chain(screen); - chain->process(prim, view, screen, m_textures, window, bgfx_util::get_blend_state(PRIMFLAG_GET_BLENDMODE(prim.m_flags))); + chain->process(prim, view, screen, m_textures, window); view += chain->applicable_passes(); } @@ -359,7 +429,7 @@ uint32_t chain_manager::count_screens(render_primitive* prim) if (screen_count > 0) { update_screen_count(screen_count); - m_targets.update_screen_count(screen_count); + m_targets.update_screen_count(screen_count, m_user_prescale, m_max_prescale_size); } if (screen_count < m_screen_prims.size()) @@ -381,8 +451,12 @@ void chain_manager::update_screen_count(uint32_t screen_count) while (m_screen_chains.size() < m_screen_count) { m_screen_chains.push_back(nullptr); - m_chain_names.push_back(""); - m_current_chain.push_back(CHAIN_NONE); + + int32_t chain_index = CHAIN_NONE; + std::string chain_name; + get_default_chain_info(chain_name, chain_index); + m_chain_names.emplace_back(std::move(chain_name)); + m_current_chain.push_back(chain_index); } // Ensure we have a screen chain selection slider per screen @@ -395,11 +469,20 @@ void chain_manager::update_screen_count(uint32_t screen_count) } } +void chain_manager::set_current_chain(uint32_t screen, int32_t chain_index) +{ + if (chain_index < m_available_chains.size() && screen < m_current_chain.size() && screen < m_chain_names.size()) + { + m_current_chain[screen] = chain_index; + m_chain_names[screen] = m_available_chains[chain_index].m_name; + } +} + int32_t chain_manager::slider_changed(int id, std::string *str, int32_t newval) { if (newval != SLIDER_NOCHANGE) { - m_current_chain[id] = newval; + set_current_chain(id, newval); std::vector<std::vector<float>> settings = slider_settings(); reload_chains(); @@ -428,11 +511,11 @@ void chain_manager::create_selection_slider(uint32_t screen_index) int32_t maxval = m_available_chains.size() - 1; int32_t incval = 1; - std::string description = "Window " + std::to_string(m_window_index) + ", Screen " + std::to_string(screen_index) + " Effect:"; - using namespace std::placeholders; - auto state = std::make_unique<slider_state>(std::move(description), minval, defval, maxval, incval, - std::bind(&chain_manager::slider_changed, this, screen_index, _1, _2)); + auto state = std::make_unique<slider_state>( + util::string_format("Window %1$u, Screen %2$u Effect", m_window_index, screen_index), + minval, defval, maxval, incval, + std::bind(&chain_manager::slider_changed, this, screen_index, _1, _2)); ui::menu_item item(ui::menu_item_type::SLIDER, state.get()); item.set_text(state->description); @@ -457,23 +540,17 @@ uint32_t chain_manager::update_screen_textures(uint32_t view, render_primitive * const uint32_t src_format = (prim.m_flags & PRIMFLAG_TEXFORMAT_MASK) >> PRIMFLAG_TEXFORMAT_SHIFT; const bool needs_conversion = m_converters[src_format] != nullptr; const bool needs_adjust = prim.m_prim->texture.palette != nullptr && src_format != TEXFORMAT_PALETTE16; - std::string screen_index = std::to_string(screen); - std::string source_name = "source" + screen_index; - std::string screen_name = "screen" + screen_index; - std::string palette_name = "palette" + screen_index; - std::string full_name = (needs_conversion || needs_adjust) ? source_name : screen_name; + const std::string screen_index = std::to_string(screen); + const std::string source_name = "source" + screen_index; + const std::string screen_name = "screen" + screen_index; + const std::string palette_name = "palette" + screen_index; + const std::string &full_name = (needs_conversion || needs_adjust) ? source_name : screen_name; if (texture && (texture->width() != tex_width || texture->height() != tex_height)) { - m_textures.add_provider(full_name, nullptr); - delete texture; + m_textures.remove_provider(full_name); + m_textures.remove_provider(palette_name); texture = nullptr; - - if (palette) - { - m_textures.add_provider(palette_name, nullptr); - delete palette; - palette = nullptr; - } + palette = nullptr; } bgfx::TextureFormat::Enum dst_format = bgfx::TextureFormat::BGRA8; @@ -481,36 +558,38 @@ uint32_t chain_manager::update_screen_textures(uint32_t view, render_primitive * int width_div_factor = 1; int width_mul_factor = 1; const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, prim.m_flags & PRIMFLAG_TEXFORMAT_MASK, - prim.m_rowpixels, tex_height, prim.m_prim->texture.palette, prim.m_prim->texture.base, pitch, width_div_factor, width_mul_factor); + prim.m_rowpixels, prim.m_prim->texture.width_margin, tex_height, prim.m_prim->texture.palette, prim.m_prim->texture.base, pitch, width_div_factor, width_mul_factor); - if (texture == nullptr) + if (!texture) { uint32_t flags = BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT; if (!PRIMFLAG_GET_TEXWRAP(prim.m_flags)) flags |= BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP; - bgfx_texture *texture = new bgfx_texture(full_name, dst_format, tex_width, tex_height, mem, flags, pitch, prim.m_rowpixels, width_div_factor, width_mul_factor); - m_textures.add_provider(full_name, texture); + auto newtex = std::make_unique<bgfx_texture>(full_name, dst_format, tex_width, prim.m_prim->texture.width_margin, tex_height, mem, flags, pitch, prim.m_rowpixels, width_div_factor, width_mul_factor); + texture = newtex.get(); + m_textures.add_provider(full_name, std::move(newtex)); if (prim.m_prim->texture.palette) { - uint16_t palette_width = (uint16_t)std::min(prim.m_palette_length, 256U); - uint16_t palette_height = (uint16_t)std::max((prim.m_palette_length + 255) / 256, 1U); + uint16_t palette_width = uint16_t(std::min(prim.m_palette_length, 256U)); + uint16_t palette_height = uint16_t(std::max((prim.m_palette_length + 255) / 256, 1U)); m_palette_temp.resize(palette_width * palette_height * 4); memcpy(&m_palette_temp[0], prim.m_prim->texture.palette, prim.m_palette_length * 4); const bgfx::Memory *palmem = bgfx::copy(&m_palette_temp[0], palette_width * palette_height * 4); - palette = new bgfx_texture(palette_name, bgfx::TextureFormat::BGRA8, palette_width, palette_height, palmem, BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT, palette_width * 4); - m_textures.add_provider(palette_name, palette); + auto newpal = std::make_unique<bgfx_texture>(palette_name, bgfx::TextureFormat::BGRA8, palette_width, 0, palette_height, palmem, BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT, palette_width * 4); + palette = newpal.get(); + m_textures.add_provider(palette_name, std::move(newpal)); } while (screen >= m_screen_textures.size()) { - m_screen_textures.push_back(nullptr); + m_screen_textures.emplace_back(nullptr); } m_screen_textures[screen] = texture; while (screen >= m_screen_palettes.size()) { - m_screen_palettes.push_back(nullptr); + m_screen_palettes.emplace_back(nullptr); } if (palette) { @@ -519,12 +598,12 @@ uint32_t chain_manager::update_screen_textures(uint32_t view, render_primitive * } else { - texture->update(mem, pitch); + texture->update(mem, pitch, prim.m_prim->texture.width_margin); if (prim.m_prim->texture.palette) { - uint16_t palette_width = (uint16_t)std::min(prim.m_palette_length, 256U); - uint16_t palette_height = (uint16_t)std::max((prim.m_palette_length + 255) / 256, 1U); + uint16_t palette_width = uint16_t(std::min(prim.m_palette_length, 256U)); + uint16_t palette_height = uint16_t(std::max((prim.m_palette_length + 255) / 256, 1U)); const uint32_t palette_size = palette_width * palette_height * 4; m_palette_temp.resize(palette_size); memcpy(&m_palette_temp[0], prim.m_prim->texture.palette, prim.m_palette_length * 4); @@ -536,26 +615,29 @@ uint32_t chain_manager::update_screen_textures(uint32_t view, render_primitive * } else { - palette = new bgfx_texture(palette_name, bgfx::TextureFormat::BGRA8, palette_width, palette_height, palmem, BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT, palette_width * 4); - m_textures.add_provider(palette_name, palette); + auto newpal = std::make_unique<bgfx_texture>(palette_name, bgfx::TextureFormat::BGRA8, palette_width, 0, palette_height, palmem, BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT, palette_width * 4); + palette = newpal.get(); + m_textures.add_provider(palette_name, std::move(newpal)); while (screen >= m_screen_palettes.size()) { - m_screen_palettes.push_back(nullptr); + m_screen_palettes.emplace_back(nullptr); } m_screen_palettes[screen] = palette; } } } + const bool has_tint = (prim.m_prim->color.a != 1.0f) || (prim.m_prim->color.r != 1.0f) || (prim.m_prim->color.g != 1.0f) || (prim.m_prim->color.b != 1.0f); bgfx_chain* chain = screen_chain(screen); if (chain && needs_adjust && !chain->has_adjuster()) { - chain->insert_effect(chain->has_converter() ? 1 : 0, m_adjuster, "XXadjust", needs_conversion ? "screen" : "source", *this); + const bool apply_tint = !needs_conversion && has_tint; + chain->insert_effect(chain->has_converter() ? 1 : 0, m_adjuster, apply_tint, "XXadjust", needs_conversion ? "screen" : "source", *this); chain->set_has_adjuster(true); } if (chain && needs_conversion && !chain->has_converter()) { - chain->insert_effect(0, m_converters[src_format], "XXconvert", "source", *this); + chain->insert_effect(0, m_converters[src_format], has_tint, "XXconvert", "source", *this); chain->set_has_converter(true); } } @@ -583,7 +665,7 @@ uint32_t chain_manager::process_screen_chains(uint32_t view, osd_window& window) std::swap(screen_width, screen_height); } - const bool any_targets_rebuilt = m_targets.update_target_sizes(screen_index, screen_width, screen_height, TARGET_STYLE_NATIVE); + const bool any_targets_rebuilt = m_targets.update_target_sizes(screen_index, screen_width, screen_height, TARGET_STYLE_NATIVE, m_user_prescale, m_max_prescale_size); if (any_targets_rebuilt) { for (bgfx_chain* chain : m_screen_chains) @@ -608,12 +690,12 @@ uint32_t chain_manager::process_screen_chains(uint32_t view, osd_window& window) bool chain_manager::has_applicable_chain(uint32_t screen) { - return screen < m_screen_count && m_current_chain[screen] != CHAIN_NONE && m_screen_chains[screen] != nullptr; + return (screen < m_screen_count) && (m_current_chain[screen] != CHAIN_NONE) && m_screen_chains[screen]; } bool chain_manager::needs_sliders() { - return m_screen_count > 0 && m_available_chains.size() > 1; + return (m_screen_count > 0) && (m_available_chains.size() > 1); } void chain_manager::restore_slider_settings(int32_t id, std::vector<std::vector<float>>& settings) @@ -636,7 +718,7 @@ void chain_manager::restore_slider_settings(int32_t id, std::vector<std::vector< continue; } - std::vector<bgfx_slider*> chain_sliders = chain->sliders(); + const std::vector<bgfx_slider*> &chain_sliders = chain->sliders(); for (size_t slider = 0; slider < chain_sliders.size(); slider++) { chain_sliders[slider]->import(settings[index][slider]); @@ -644,6 +726,151 @@ void chain_manager::restore_slider_settings(int32_t id, std::vector<std::vector< } } +void chain_manager::load_config(util::xml::data_node const &windownode) +{ + // treat source INI files or more specific as higher priority than CFG + // FIXME: leaky abstraction - this depends on a front-end implementation detail + bool const persist = windownode.get_attribute_int("persist", 1) != 0; + bool const default_chains = (OPTION_PRIORITY_NORMAL + 5) > m_options.get_entry(OSDOPTION_BGFX_SCREEN_CHAINS)->priority(); + bool const explicit_chains = !persist && !default_chains && *m_options.bgfx_screen_chains(); + + // if chains weren't explicitly specified, restore the chains from the config file + if (explicit_chains) + { + osd_printf_verbose( + "BGFX: Ignoring chain selection from window 0 configuration due to explicitly specified chains\n", + m_window_index); + } + else + { + bool changed = false; + util::xml::data_node const *screennode = windownode.get_child("screen"); + while (screennode) + { + auto const index = screennode->get_attribute_int("index", -1); + if ((0 <= index) && (m_screen_count > index)) + { + char const *const chainname = screennode->get_attribute_string("chain", nullptr); + if (chainname) + { + auto const found = std::find_if( + m_available_chains.begin(), + m_available_chains.end(), + [&chainname] (auto const &avail) { return avail.m_name == chainname; }); + if (m_available_chains.end() != found) + { + auto const chainnum = found - m_available_chains.begin(); + if (chainnum != m_current_chain[index]) + { + m_current_chain[index] = chainnum; + changed = true; + } + } + } + } + + screennode = screennode->get_next_sibling("screen"); + } + + if (changed) + reload_chains(); + } + + // now apply slider settings for screens with chains matching config + util::xml::data_node const *screennode = windownode.get_child("screen"); + while (screennode) + { + auto const index = screennode->get_attribute_int("index", -1); + if ((0 <= index) && (m_screen_count > index) && (m_screen_chains.size() > index)) + { + bgfx_chain *const chain = m_screen_chains[index]; + char const *const chainname = screennode->get_attribute_string("chain", nullptr); + if (chain && chainname && (m_available_chains[m_current_chain[index]].m_name == chainname)) + { + auto const &sliders = chain->sliders(); + + util::xml::data_node const *slidernode = screennode->get_child("slider"); + while (slidernode) + { + char const *const slidername = slidernode->get_attribute_string("name", nullptr); + if (slidername) + { + auto const found = std::find_if( + sliders.begin(), + sliders.end(), + [&slidername] (auto const &slider) { return slider->name() == slidername; }); + if (sliders.end() != found) + { + bgfx_slider &slider = **found; + switch (slider.type()) + { + case bgfx_slider::SLIDER_INT_ENUM: + case bgfx_slider::SLIDER_INT: + { + slider_state const &core = *slider.core_slider(); + int32_t const val = slidernode->get_attribute_int("value", core.defval); + slider.update(nullptr, std::clamp(val, core.minval, core.maxval)); + } + break; + default: + { + float const val = slidernode->get_attribute_float("value", slider.default_value()); + slider.import(std::clamp(val, slider.min_value(), slider.max_value())); + } + } + } + } + + slidernode = slidernode->get_next_sibling("slider"); + } + } + } + screennode = screennode->get_next_sibling("screen"); + } +} + +void chain_manager::save_config(util::xml::data_node &parentnode) +{ + if (!needs_sliders()) + return; + + util::xml::data_node *const windownode = parentnode.add_child("window", nullptr); + windownode->set_attribute_int("index", m_window_index); + + for (size_t index = 0; index < m_screen_chains.size() && index < m_screen_count; index++) + { + bgfx_chain *const chain = m_screen_chains[index]; + if (!chain) + continue; + + util::xml::data_node *const screennode = windownode->add_child("screen", nullptr); + screennode->set_attribute_int("index", index); + screennode->set_attribute("chain", m_available_chains[m_current_chain[index]].m_name.c_str()); + + for (bgfx_slider *slider : chain->sliders()) + { + auto const val = slider->update(nullptr, SLIDER_NOCHANGE); + if (val == slider->core_slider()->defval) + continue; + + util::xml::data_node *const slidernode = screennode->add_child("slider", nullptr); + slidernode->set_attribute("name", slider->name().c_str()); + switch (slider->type()) + { + case bgfx_slider::SLIDER_INT_ENUM: + case bgfx_slider::SLIDER_INT: + slidernode->set_attribute_int("value", val); + break; + default: + slidernode->set_attribute_float("value", slider->value()); + } + } + } + + if (!windownode->get_first_child()) + windownode->delete_node(); +} + std::vector<std::vector<float>> chain_manager::slider_settings() { std::vector<std::vector<float>> curr; @@ -663,7 +890,7 @@ std::vector<std::vector<float>> chain_manager::slider_settings() continue; } - std::vector<bgfx_slider*> chain_sliders = chain->sliders(); + const std::vector<bgfx_slider*> &chain_sliders = chain->sliders(); for (bgfx_slider* slider : chain_sliders) { curr[index].push_back(slider->value()); @@ -692,24 +919,24 @@ std::vector<ui::menu_item> chain_manager::get_slider_list() continue; } - std::vector<bgfx_chain_entry*> chain_entries = chain->entries(); + const std::vector<bgfx_chain_entry*> &chain_entries = chain->entries(); for (bgfx_chain_entry* entry : chain_entries) { - std::vector<bgfx_input_pair*> entry_inputs = entry->inputs(); + const std::vector<bgfx_input_pair*> &entry_inputs = entry->inputs(); for (bgfx_input_pair* input : entry_inputs) { std::vector<ui::menu_item> input_sliders = input->get_slider_list(); - for (ui::menu_item slider : input_sliders) + for (ui::menu_item &slider : input_sliders) { sliders.emplace_back(slider); } } } - std::vector<bgfx_slider*> chain_sliders = chain->sliders(); + const std::vector<bgfx_slider*> &chain_sliders = chain->sliders(); for (bgfx_slider* slider : chain_sliders) { - slider_state* core_slider = slider->core_slider(); + slider_state *const core_slider = slider->core_slider(); ui::menu_item item(ui::menu_item_type::SLIDER, core_slider); item.set_text(core_slider->description); diff --git a/src/osd/modules/render/bgfx/chainmanager.h b/src/osd/modules/render/bgfx/chainmanager.h index 59a65dc59cf..0d489d0bd72 100644 --- a/src/osd/modules/render/bgfx/chainmanager.h +++ b/src/osd/modules/render/bgfx/chainmanager.h @@ -9,19 +9,24 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_CHAINMANAGER_H +#define MAME_RENDER_BGFX_CHAINMANAGER_H -#ifndef __DRAWBGFX_CHAIN_MANAGER__ -#define __DRAWBGFX_CHAIN_MANAGER__ +#pragma once -#include "texturemanager.h" -#include "targetmanager.h" #include "effectmanager.h" +#include "targetmanager.h" +#include "texturemanager.h" + +#include "util/utilfwd.h" #include <map> #include <string> +#include <string_view> +#include <utility> #include <vector> + class running_machine; class osd_window; struct slider_state; @@ -33,23 +38,29 @@ namespace ui { class menu_item; } class bgfx_chain; class bgfx_slider; -class chain_desc +class chain_manager { public: - chain_desc(std::string name, std::string path) - : m_name(name) - , m_path(path) + class screen_prim { - } + public: + screen_prim() = default; + screen_prim(render_primitive *prim); - const std::string m_name; - const std::string m_path; -}; + render_primitive *m_prim = nullptr; + uint16_t m_screen_width = 0; + uint16_t m_screen_height = 0; + uint16_t m_quad_width = 0; + uint16_t m_quad_height = 0; + float m_tex_width = 0.0f; + float m_tex_height = 0.0f; + int m_rowpixels = 0; + uint32_t m_palette_length = 0; + uint32_t m_flags = 0; + }; -class chain_manager -{ -public: - chain_manager(running_machine& machine, osd_options& options, texture_manager& textures, target_manager& targets, effect_manager& effects, uint32_t window_index, slider_dirty_notifier& slider_notifier); + chain_manager(running_machine& machine, const osd_options& options, texture_manager& textures, target_manager& targets, effect_manager& effects, uint32_t window_index, + slider_dirty_notifier& slider_notifier, uint16_t user_prescale, uint16_t max_prescale_size); ~chain_manager(); uint32_t update_screen_textures(uint32_t view, render_primitive *starting_prim, osd_window& window); @@ -57,7 +68,7 @@ public: // Getters running_machine& machine() const { return m_machine; } - osd_options& options() const { return m_options; } + const osd_options& options() const { return m_options; } texture_manager& textures() const { return m_textures; } target_manager& targets() const { return m_targets; } effect_manager& effects() const { return m_effects; } @@ -65,7 +76,7 @@ public: uint32_t window_index() const { return m_window_index; } uint32_t screen_count() const { return m_screen_count; } bgfx_chain* screen_chain(uint32_t screen); - bgfx_chain* load_chain(std::string name, uint32_t screen_index); + std::unique_ptr<bgfx_chain> load_chain(std::string name, uint32_t screen_index); bool has_applicable_chain(uint32_t screen); std::vector<ui::menu_item> get_slider_list(); std::vector<std::vector<float>> slider_settings(); @@ -73,43 +84,44 @@ public: // Setters void restore_slider_settings(int32_t id, std::vector<std::vector<float>>& settings); - class screen_prim + void load_config(util::xml::data_node const &screennode); + void save_config(util::xml::data_node &parentnode); + +private: + class chain_desc { public: - screen_prim() : m_prim(nullptr), m_screen_width(0), m_screen_height(0), m_quad_width(0), m_quad_height(0) - , m_tex_width(0), m_tex_height(0), m_rowpixels(0), m_palette_length(0), m_flags(0) + chain_desc(const chain_desc &) = default; + chain_desc(chain_desc &&) = default; + chain_desc &operator=(const chain_desc &) = default; + chain_desc &operator=(chain_desc &&) = default; + + chain_desc(std::string &&name, std::string &&path) + : m_name(std::move(name)) + , m_path(std::move(path)) { } - screen_prim(render_primitive *prim); - - render_primitive *m_prim; - uint16_t m_screen_width; - uint16_t m_screen_height; - uint16_t m_quad_width; - uint16_t m_quad_height; - float m_tex_width; - float m_tex_height; - int m_rowpixels; - uint32_t m_palette_length; - uint32_t m_flags; + std::string m_name; + std::string m_path; }; -private: void load_chains(); void destroy_chains(); void reload_chains(); void init_texture_converters(); + void get_default_chain_info(std::string &out_chain_name, int32_t &out_chain_index); void refresh_available_chains(); void destroy_unloaded_chains(); - void find_available_chains(std::string root, std::string path); - void parse_chain_selections(std::string chain_str); - std::vector<std::string> split_option_string(std::string chain_str) const; + void find_available_chains(std::string_view root, std::string_view path); + void parse_chain_selections(std::string_view chain_str); + std::vector<std::string_view> split_option_string(std::string_view chain_str) const; void update_screen_count(uint32_t screen_count); + void set_current_chain(uint32_t screen, int32_t chain_index); int32_t slider_changed(int id, std::string *str, int32_t newval); void create_selection_slider(uint32_t screen_index); bool needs_sliders(); @@ -118,13 +130,16 @@ private: void process_screen_quad(uint32_t view, uint32_t screen, screen_prim &prim, osd_window& window); running_machine& m_machine; - osd_options& m_options; + const osd_options& m_options; texture_manager& m_textures; target_manager& m_targets; effect_manager& m_effects; uint32_t m_window_index; + uint16_t m_user_prescale; + uint16_t m_max_prescale_size; slider_dirty_notifier& m_slider_notifier; uint32_t m_screen_count; + int32_t m_default_chain_index; std::vector<chain_desc> m_available_chains; std::vector<bgfx_chain*> m_screen_chains; std::vector<std::string> m_chain_names; @@ -138,7 +153,7 @@ private: std::vector<screen_prim> m_screen_prims; std::vector<uint8_t> m_palette_temp; - static const uint32_t CHAIN_NONE; + static inline constexpr uint32_t CHAIN_NONE = 0; }; -#endif // __DRAWBGFX_CHAIN_MANAGER__ +#endif // MAME_RENDER_BGFX_CHAINMANAGER_H diff --git a/src/osd/modules/render/bgfx/chainreader.cpp b/src/osd/modules/render/bgfx/chainreader.cpp index 92e30df0b33..362baa42c82 100644 --- a/src/osd/modules/render/bgfx/chainreader.cpp +++ b/src/osd/modules/render/bgfx/chainreader.cpp @@ -8,21 +8,26 @@ #include "chainreader.h" -#include <string> -#include <vector> -#include <map> - #include "chain.h" +#include "chainentryreader.h" #include "chainmanager.h" -#include "sliderreader.h" +#include "parameter.h" #include "paramreader.h" -#include "chainentryreader.h" -#include "targetreader.h" -#include "targetmanager.h" #include "slider.h" -#include "parameter.h" +#include "sliderreader.h" +#include "targetmanager.h" +#include "targetreader.h" + +#include <vector> +#include <map> -bgfx_chain* chain_reader::read_from_value(const Value& value, std::string prefix, chain_manager& chains, uint32_t screen_index) +std::unique_ptr<bgfx_chain> chain_reader::read_from_value( + const Value& value, + const std::string &prefix, + chain_manager& chains, + uint32_t screen_index, + uint16_t user_prescale, + uint16_t max_prescale_size) { if (!validate_parameters(value, prefix)) { @@ -96,7 +101,7 @@ bgfx_chain* chain_reader::read_from_value(const Value& value, std::string prefix // TODO: Move into its own reader for (uint32_t i = 0; i < target_array.Size(); i++) { - bgfx_target* target = target_reader::read_from_value(target_array[i], prefix + "targets[" + std::to_string(i) + "]: ", chains, screen_index); + bgfx_target* target = target_reader::read_from_value(target_array[i], prefix + "targets[" + std::to_string(i) + "]: ", chains, screen_index, user_prescale, max_prescale_size); if (target == nullptr) { return nullptr; @@ -121,19 +126,28 @@ bgfx_chain* chain_reader::read_from_value(const Value& value, std::string prefix } } - return new bgfx_chain(name, author, transform, chains.targets(), sliders, parameters, entries, target_list, screen_index); + return std::make_unique<bgfx_chain>( + std::move(name), + std::move(author), + transform, + chains.targets(), + std::move(sliders), + std::move(parameters), + std::move(entries), + std::move(target_list), + screen_index); } -bool chain_reader::validate_parameters(const Value& value, std::string prefix) +bool chain_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'name'\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("author"), (prefix + "Must have string value 'author'\n").c_str())) return false; - if (!READER_CHECK(value["author"].IsString(), (prefix + "Value 'author' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("passes"), (prefix + "Must have array value 'passes'\n").c_str())) return false; - if (!READER_CHECK(value["passes"].IsArray(), (prefix + "Value 'passes' must be an array\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("sliders") || value["sliders"].IsArray(), (prefix + "Value 'sliders' must be an array\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("parameters") || value["parameters"].IsArray(), (prefix + "Value 'parameters' must be an array\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("targets") || value["targets"].IsArray(), (prefix + "Value 'targets' must be an array\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("name"), "%sMust have string value 'name'\n", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%sValue 'name' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("author"), "%sMust have string value 'author'\n", prefix)) return false; + if (!READER_CHECK(value["author"].IsString(), "%sValue 'author' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("passes"), "%sMust have array value 'passes'\n", prefix)) return false; + if (!READER_CHECK(value["passes"].IsArray(), "%sValue 'passes' must be an array\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("sliders") || value["sliders"].IsArray(), "%sValue 'sliders' must be an array\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("parameters") || value["parameters"].IsArray(), "%sValue 'parameters' must be an array\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("targets") || value["targets"].IsArray(), "%sValue 'targets' must be an array\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/chainreader.h b/src/osd/modules/render/bgfx/chainreader.h index a2fa3ba28a5..6bbd2058501 100644 --- a/src/osd/modules/render/bgfx/chainreader.h +++ b/src/osd/modules/render/bgfx/chainreader.h @@ -6,23 +6,26 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_CHAINREADER_H +#define MAME_RENDER_BGFX_CHAINREADER_H -#ifndef __DRAWBGFX_CHAIN_READER__ -#define __DRAWBGFX_CHAIN_READER__ +#pragma once #include "statereader.h" +#include <memory> +#include <string> + class bgfx_chain; class chain_manager; class chain_reader : public state_reader { public: - static bgfx_chain* read_from_value(const Value& value, std::string prefix, chain_manager& chains, uint32_t screen_index); + static std::unique_ptr<bgfx_chain> read_from_value(const Value& value, const std::string &prefix, chain_manager& chains, uint32_t screen_index, uint16_t user_prescale, uint16_t max_prescale_size); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_CHAIN_READER__ +#endif // MAME_RENDER_BGFX_CHAINREADER_H diff --git a/src/osd/modules/render/bgfx/clearreader.cpp b/src/osd/modules/render/bgfx/clearreader.cpp index 948d96a7393..58c4d5f01aa 100644 --- a/src/osd/modules/render/bgfx/clearreader.cpp +++ b/src/osd/modules/render/bgfx/clearreader.cpp @@ -6,13 +6,15 @@ // //============================================================ -#include <bgfx/bgfx.h> - #include "clearreader.h" #include "clear.h" -clear_state* clear_reader::read_from_value(const Value& value, std::string prefix) +#include <bgfx/bgfx.h> + +#include <algorithm> + +clear_state* clear_reader::read_from_value(const Value& value, const std::string &prefix) { if (!validate_parameters(value, prefix)) { @@ -29,10 +31,8 @@ clear_state* clear_reader::read_from_value(const Value& value, std::string prefi const Value& colors = value["clearcolor"]; for (int i = 0; i < colors.Size(); i++) { - if (!READER_CHECK(colors[i].IsNumber(), (prefix + "clearcolor[" + std::to_string(i) + "] must be a numeric value\n").c_str())) return nullptr; - auto val = int32_t(float(colors[i].GetDouble()) * 255.0f); - if (val > 255) val = 255; - if (val < 0) val = 0; + if (!READER_CHECK(colors[i].IsNumber(), "%sclearcolor[%d] must be a numeric value\n", prefix, i)) return nullptr; + auto val = std::clamp<int32_t>(float(colors[i].GetDouble()) * 255.0f, 0, 255); clear_color |= val << (24 - (i * 3)); } clear_flags |= BGFX_CLEAR_COLOR; @@ -53,10 +53,10 @@ clear_state* clear_reader::read_from_value(const Value& value, std::string prefi return new clear_state(clear_flags, clear_color, clear_depth, clear_stencil); } -bool clear_reader::validate_parameters(const Value& value, std::string prefix) +bool clear_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(!value.HasMember("clearcolor") || (value["clearcolor"].IsArray() && value["clearcolor"].GetArray().Size() == 4), (prefix + "'clearcolor' must be an array of four numeric RGBA values representing the color to which to clear the color buffer\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("cleardepth") || value["cleardepth"].IsNumber(), (prefix + "'cleardepth' must be a numeric value representing the depth to which to clear the depth buffer\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("clearstencil") || value["clearstencil"].IsNumber(), (prefix + "'clearstencil' must be a numeric value representing the stencil value to which to clear the stencil buffer\n").c_str())) return false; + if (!READER_CHECK(!value.HasMember("clearcolor") || (value["clearcolor"].IsArray() && value["clearcolor"].GetArray().Size() == 4), "%s'clearcolor' must be an array of four numeric RGBA values representing the color to which to clear the color buffer\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("cleardepth") || value["cleardepth"].IsNumber(), "%s'cleardepth' must be a numeric value representing the depth to which to clear the depth buffer\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("clearstencil") || value["clearstencil"].IsNumber(), "%s'clearstencil' must be a numeric value representing the stencil value to which to clear the stencil buffer\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/clearreader.h b/src/osd/modules/render/bgfx/clearreader.h index 80605ea35f3..1c2063e95de 100644 --- a/src/osd/modules/render/bgfx/clearreader.h +++ b/src/osd/modules/render/bgfx/clearreader.h @@ -6,26 +6,26 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_CLEARREADER_H +#define MAME_RENDER_BGFX_CLEARREADER_H + #pragma once -#ifndef __DRAWBGFX_CLEAR_READER__ -#define __DRAWBGFX_CLEAR_READER__ +#include "statereader.h" #include <string> -#include "statereader.h" - class clear_state; class clear_reader : public state_reader { public: - static clear_state* read_from_value(const Value& value, std::string prefix); + static clear_state* read_from_value(const Value& value, const std::string &prefix); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); static const int FLAG_COUNT = 3; static const string_to_enum FLAG_NAMES[FLAG_COUNT]; }; -#endif // __DRAWBGFX_CLEAR_READER__ +#endif // MAME_RENDER_BGFX_CLEARREADER_H diff --git a/src/osd/modules/render/bgfx/depthreader.cpp b/src/osd/modules/render/bgfx/depthreader.cpp index 97502d9028e..bcfc568b0da 100644 --- a/src/osd/modules/render/bgfx/depthreader.cpp +++ b/src/osd/modules/render/bgfx/depthreader.cpp @@ -6,10 +6,10 @@ // //============================================================ -#include <bgfx/bgfx.h> - #include "depthreader.h" +#include <bgfx/bgfx.h> + const depth_reader::string_to_enum depth_reader::FUNCTION_NAMES[depth_reader::FUNCTION_COUNT] = { { "never", BGFX_STATE_DEPTH_TEST_NEVER }, { "less", BGFX_STATE_DEPTH_TEST_LESS }, @@ -21,12 +21,12 @@ const depth_reader::string_to_enum depth_reader::FUNCTION_NAMES[depth_reader::FU { "always", BGFX_STATE_DEPTH_TEST_ALWAYS } }; -uint64_t depth_reader::read_from_value(const Value& value, std::string prefix) +uint64_t depth_reader::read_from_value(const Value& value, const std::string &prefix) { uint64_t write_enable = 0; if (value.HasMember("writeenable")) { - if (!READER_CHECK(value["writeenable"].IsBool(), (prefix + "Value 'writeenable' must be a boolean\n").c_str())) return 0; + if (!READER_CHECK(value["writeenable"].IsBool(), "%sValue 'writeenable' must be a boolean\n", prefix)) return 0; write_enable = value["writeenable"].GetBool() ? BGFX_STATE_WRITE_Z : 0; } diff --git a/src/osd/modules/render/bgfx/depthreader.h b/src/osd/modules/render/bgfx/depthreader.h index 9e2c99a0b29..0815732850f 100644 --- a/src/osd/modules/render/bgfx/depthreader.h +++ b/src/osd/modules/render/bgfx/depthreader.h @@ -6,10 +6,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_DEPTHREADER_H +#define MAME_RENDER_BGFX_DEPTHREADER_H -#ifndef __DRAWBGFX_DEPTH_READER__ -#define __DRAWBGFX_DEPTH_READER__ +#pragma once #include <string> @@ -17,11 +17,11 @@ class depth_reader : public state_reader { public: - static uint64_t read_from_value(const Value& value, std::string prefix); + static uint64_t read_from_value(const Value& value, const std::string &prefix); private: static const int FUNCTION_COUNT = 8; static const string_to_enum FUNCTION_NAMES[FUNCTION_COUNT]; }; -#endif // __DRAWBGFX_DEPTH_READER__ +#endif // MAME_RENDER_BGFX_DEPTHREADER_H diff --git a/src/osd/modules/render/bgfx/effect.cpp b/src/osd/modules/render/bgfx/effect.cpp index c5ea8555dce..18b43cca9bf 100644 --- a/src/osd/modules/render/bgfx/effect.cpp +++ b/src/osd/modules/render/bgfx/effect.cpp @@ -13,46 +13,50 @@ #include "modules/osdmodule.h" #include "osdcore.h" -bgfx_effect::bgfx_effect(uint64_t state, bgfx::ShaderHandle vertex_shader, bgfx::ShaderHandle fragment_shader, std::vector<bgfx_uniform*> uniforms) - : m_state(state) +#include <utility> + + +bgfx_effect::bgfx_effect(std::string &&name, uint64_t state, bgfx::ShaderHandle vertex_shader, bgfx::ShaderHandle fragment_shader, std::vector<std::unique_ptr<bgfx_uniform> > &uniforms) + : m_name(std::move(name)) + , m_state(state) { m_program_handle = bgfx::createProgram(vertex_shader, fragment_shader, false); - for (int i = 0; i < uniforms.size(); i++) + for (auto &uniform : uniforms) { - if (m_uniforms[uniforms[i]->name()] != nullptr) + const auto existing = m_uniforms.find(uniform->name()); + if (existing != m_uniforms.end()) { - osd_printf_verbose("Uniform %s appears to be duplicated in one or more effects, please double-check the effect JSON files.\n", uniforms[i]->name()); - delete uniforms[i]; + osd_printf_verbose("Uniform %s appears to be duplicated in one or more effects, please double-check the effect JSON files.\n", uniform->name()); + uniform.reset(); continue; } - uniforms[i]->create(); - m_uniforms[uniforms[i]->name()] = uniforms[i]; + uniform->create(); + m_uniforms.emplace(uniform->name(), std::move(uniform)); } } bgfx_effect::~bgfx_effect() { - for (std::pair<std::string, bgfx_uniform*> uniform : m_uniforms) - { - delete uniform.second; - } m_uniforms.clear(); bgfx::destroy(m_program_handle); } void bgfx_effect::submit(int view, uint64_t blend) { - for (std::pair<std::string, bgfx_uniform*> uniform_pair : m_uniforms) + for (auto &[name, uniform] : m_uniforms) { - (uniform_pair.second)->upload(); + uniform->upload(); } - bgfx::setState(m_state | blend); + + const uint64_t final_state = (blend != ~0ULL) ? ((m_state & ~BGFX_STATE_BLEND_MASK) | blend) : m_state; + + bgfx::setState(final_state); bgfx::submit(view, m_program_handle); } -bgfx_uniform* bgfx_effect::uniform(std::string name) +bgfx_uniform* bgfx_effect::uniform(const std::string &name) { - auto iter = m_uniforms.find(name); - return iter != m_uniforms.end() ? iter->second : nullptr; + const auto iter = m_uniforms.find(name); + return (iter != m_uniforms.end()) ? iter->second.get() : nullptr; } diff --git a/src/osd/modules/render/bgfx/effect.h b/src/osd/modules/render/bgfx/effect.h index ca1be77c73d..07646e6302e 100644 --- a/src/osd/modules/render/bgfx/effect.h +++ b/src/osd/modules/render/bgfx/effect.h @@ -6,33 +6,36 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_EFFECT_H +#define MAME_RENDER_BGFX_EFFECT_H -#ifndef __DRAWBGFX_EFFECT__ -#define __DRAWBGFX_EFFECT__ +#pragma once #include <bgfx/bgfx.h> -#include <vector> #include <map> +#include <memory> #include <string> +#include <vector> + class bgfx_uniform; class bgfx_effect { public: - bgfx_effect(uint64_t state, bgfx::ShaderHandle vertex_shader, bgfx::ShaderHandle fragment_shader, std::vector<bgfx_uniform*> uniforms); + bgfx_effect(std::string &&name, uint64_t state, bgfx::ShaderHandle vertex_shader, bgfx::ShaderHandle fragment_shader, std::vector<std::unique_ptr<bgfx_uniform> > &uniforms); ~bgfx_effect(); - void submit(int view, uint64_t blend = 0L); - bgfx_uniform *uniform(std::string name); - bool is_valid() { return m_program_handle.idx != bgfx::kInvalidHandle; } + void submit(int view, uint64_t blend = ~0ULL); + bgfx_uniform *uniform(const std::string &name); + bool is_valid() const { return m_program_handle.idx != bgfx::kInvalidHandle; } private: + std::string m_name; uint64_t m_state; bgfx::ProgramHandle m_program_handle; - std::map<std::string, bgfx_uniform*> m_uniforms; + std::map<std::string, std::unique_ptr<bgfx_uniform> > m_uniforms; }; -#endif // __DRAWBGFX_EFFECT__ +#endif // MAME_RENDER_BGFX_EFFECT_H diff --git a/src/osd/modules/render/bgfx/effectmanager.cpp b/src/osd/modules/render/bgfx/effectmanager.cpp index f57f56f708d..e5e5d1698f6 100644 --- a/src/osd/modules/render/bgfx/effectmanager.cpp +++ b/src/osd/modules/render/bgfx/effectmanager.cpp @@ -11,8 +11,11 @@ #include "effectmanager.h" -#include "effectreader.h" #include "effect.h" +#include "effectreader.h" +#include "shadermanager.h" + +#include "path.h" #include "osdfile.h" #include "modules/lib/osdobj_common.h" @@ -25,66 +28,75 @@ #include <bgfx/bgfx.h> -static bool prepare_effect_document(std::string &name, osd_options &options, rapidjson::Document &document) +#include <utility> + + +static bool prepare_effect_document(const std::string &name, const osd_options &options, rapidjson::Document &document) { std::string full_name = name; if (full_name.length() < 5 || (full_name.compare(full_name.length() - 5, 5, ".json") != 0)) { - full_name = full_name + ".json"; + full_name += ".json"; } - std::string path = osd_subst_env(util::string_format("%s" PATH_SEPARATOR "effects" PATH_SEPARATOR, options.bgfx_path())); - path += full_name; + const std::string path = util::path_concat(options.bgfx_path(), "effects", full_name); bx::FileReader reader; if (!bx::open(&reader, path.c_str())) { - osd_printf_error("Unable to open effect file %s\n", path.c_str()); + osd_printf_error("Unable to open effect file %s\n", path); return false; } - int32_t size (bx::getSize(&reader)); - char* data = new char[size + 1]; - bx::read(&reader, reinterpret_cast<void*>(data), size); + const int32_t size = bx::getSize(&reader); + std::unique_ptr<char []> data(new (std::nothrow) char [size + 1]); + if (!data) + { + osd_printf_error("Out of memory reading effect file %s\n", path); + bx::close(&reader); + return false; + } + + bx::ErrorAssert err; + bx::read(&reader, reinterpret_cast<void*>(data.get()), size, &err); bx::close(&reader); data[size] = 0; - document.Parse<rapidjson::kParseCommentsFlag>(data); - - delete [] data; + document.Parse<rapidjson::kParseCommentsFlag>(data.get()); + data.reset(); if (document.HasParseError()) { std::string error(rapidjson::GetParseError_En(document.GetParseError())); - osd_printf_error("Unable to parse effect %s. Errors returned:\n", path.c_str()); - osd_printf_error("%s\n", error.c_str()); + osd_printf_error("Unable to parse effect %s. Errors returned:\n%s\n", path, error); return false; } return true; } + +// keep constructor and destructor out-of-line so the header works with forward declarations + +effect_manager::effect_manager(shader_manager& shaders) : m_shaders(shaders) +{ +} + effect_manager::~effect_manager() { - for (std::pair<std::string, bgfx_effect*> effect : m_effects) - { - delete effect.second; - } - m_effects.clear(); + // the map will automatically dispose of the effects } -bgfx_effect* effect_manager::get_or_load_effect(osd_options &options, std::string name) +bgfx_effect* effect_manager::get_or_load_effect(const osd_options &options, const std::string &name) { - std::map<std::string, bgfx_effect*>::iterator iter = m_effects.find(name); + const auto iter = m_effects.find(name); if (iter != m_effects.end()) - { - return iter->second; - } + return iter->second.get(); return load_effect(options, name); } -bgfx_effect* effect_manager::load_effect(osd_options &options, std::string name) +bgfx_effect* effect_manager::load_effect(const osd_options &options, const std::string &name) { rapidjson::Document document; if (!prepare_effect_document(name, options, document)) @@ -92,20 +104,18 @@ bgfx_effect* effect_manager::load_effect(osd_options &options, std::string name) return nullptr; } - bgfx_effect* effect = effect_reader::read_from_value(document, "Effect '" + name + "': ", options, m_shaders); + std::unique_ptr<bgfx_effect> effect = effect_reader::read_from_value(name, document, "Effect '" + name + "': ", options, m_shaders); - if (effect == nullptr) + if (!effect) { - osd_printf_error("Unable to load effect %s\n", name.c_str()); + osd_printf_error("Unable to load effect %s\n", name); return nullptr; } - m_effects[name] = effect; - - return effect; + return m_effects.emplace(name, std::move(effect)).first->second.get(); } -bool effect_manager::validate_effect(osd_options &options, std::string name) +bool effect_manager::validate_effect(const osd_options &options, const std::string &name) { rapidjson::Document document; if (!prepare_effect_document(name, options, document)) diff --git a/src/osd/modules/render/bgfx/effectmanager.h b/src/osd/modules/render/bgfx/effectmanager.h index 5d8bf3f5c93..697d468dc5c 100644 --- a/src/osd/modules/render/bgfx/effectmanager.h +++ b/src/osd/modules/render/bgfx/effectmanager.h @@ -9,34 +9,35 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_EFFECTMANAGER_H +#define MAME_RENDER_BGFX_EFFECTMANAGER_H -#ifndef __DRAWBGFX_EFFECT_MANAGER__ -#define __DRAWBGFX_EFFECT_MANAGER__ +#pragma once #include <map> +#include <memory> #include <string> -#include <bgfx/bgfx.h> - -#include "shadermanager.h" class bgfx_effect; +class osd_options; +class shader_manager; -class effect_manager { +class effect_manager +{ public: - effect_manager(shader_manager& shaders) : m_shaders(shaders) { } + effect_manager(shader_manager& shaders); ~effect_manager(); // Getters - bgfx_effect* get_or_load_effect(osd_options &options, std::string name); - static bool validate_effect(osd_options &options, std::string name); + bgfx_effect* get_or_load_effect(const osd_options &options, const std::string &name); + static bool validate_effect(const osd_options &options, const std::string &name); private: - bgfx_effect* load_effect(osd_options& options, std::string name); + bgfx_effect* load_effect(const osd_options &options, const std::string &name); - shader_manager& m_shaders; - std::map<std::string, bgfx_effect*> m_effects; + shader_manager &m_shaders; + std::map<std::string, std::unique_ptr<bgfx_effect> > m_effects; }; -#endif // __DRAWBGFX_EFFECT_MANAGER__ +#endif // MAME_RENDER_BGFX_EFFECTMANAGER_H diff --git a/src/osd/modules/render/bgfx/effectreader.cpp b/src/osd/modules/render/bgfx/effectreader.cpp index 36acee2f9ea..befdc598537 100644 --- a/src/osd/modules/render/bgfx/effectreader.cpp +++ b/src/osd/modules/render/bgfx/effectreader.cpp @@ -8,80 +8,79 @@ #include "effectreader.h" -#include "effect.h" #include "blendreader.h" -#include "depthreader.h" #include "cullreader.h" -#include "writereader.h" +#include "depthreader.h" +#include "effect.h" #include "shadermanager.h" -#include "uniformreader.h" #include "uniform.h" +#include "uniformreader.h" +#include "writereader.h" -bgfx_effect *effect_reader::read_from_value(const Value& value, std::string prefix, osd_options &options, shader_manager& shaders) +#include <utility> + + +std::unique_ptr<bgfx_effect> effect_reader::read_from_value( + const std::string &name, + const Value &value, + const std::string &prefix, + const osd_options &options, + shader_manager &shaders) { uint64_t flags = 0; std::string vertex_name; std::string fragment_name; - bgfx::ShaderHandle vertex_shader = BGFX_INVALID_HANDLE; - bgfx::ShaderHandle fragment_shader = BGFX_INVALID_HANDLE; - std::vector<bgfx_uniform *> uniforms; + std::vector<std::unique_ptr<bgfx_uniform> > uniforms; if (!get_base_effect_data(value, prefix, flags, vertex_name, fragment_name, uniforms)) { return nullptr; } + bgfx::ShaderHandle vertex_shader = BGFX_INVALID_HANDLE; + bgfx::ShaderHandle fragment_shader = BGFX_INVALID_HANDLE; + if (!get_shader_data(value, options, shaders, vertex_name, vertex_shader, fragment_name, fragment_shader)) { - clear_uniform_list(uniforms); return nullptr; } - bgfx_effect *effect = new bgfx_effect(flags, vertex_shader, fragment_shader, uniforms); - if (effect->is_valid()) - { - return effect; - } - - delete effect; + std::unique_ptr<bgfx_effect> effect(new bgfx_effect(std::string(name), flags, vertex_shader, fragment_shader, uniforms)); + if (!effect->is_valid()) + return nullptr; - return nullptr; + return effect; } -bool effect_reader::validate_value(const Value& value, std::string prefix, osd_options &options) +bool effect_reader::validate_value(const Value& value, const std::string &prefix, const osd_options &options) { if (!validate_parameters(value, prefix)) - { return false; - } uint64_t flags = 0; std::string vertex_name; std::string fragment_name; - std::vector<bgfx_uniform *> uniforms; + std::vector<std::unique_ptr<bgfx_uniform> > uniforms; if (!get_base_effect_data(value, prefix, flags, vertex_name, fragment_name, uniforms)) - { return false; - } if (!shader_manager::is_shader_present(options, vertex_name)) - { - clear_uniform_list(uniforms); return false; - } if (!shader_manager::is_shader_present(options, fragment_name)) - { - clear_uniform_list(uniforms); return false; - } return true; } -bool effect_reader::get_base_effect_data(const Value& value, std::string &prefix, uint64_t &flags, std::string &vertex_name, std::string &fragment_name, - std::vector<bgfx_uniform *> &uniforms) +bool effect_reader::get_base_effect_data( + const Value& value, + const std::string &prefix, + uint64_t &flags, + std::string &vertex_name, + std::string &fragment_name, + std::vector<std::unique_ptr<bgfx_uniform> > &uniforms) { if (!validate_parameters(value, prefix)) { @@ -101,12 +100,12 @@ bool effect_reader::get_base_effect_data(const Value& value, std::string &prefix const Value& uniform_array = value["uniforms"]; for (uint32_t i = 0; i < uniform_array.Size(); i++) { - bgfx_uniform *uniform = uniform_reader::read_from_value(uniform_array[i], prefix + "uniforms[" + std::to_string(i) + "]: "); - if (uniform == nullptr) + auto uniform = uniform_reader::read_from_value(uniform_array[i], prefix + "uniforms[" + std::to_string(i) + "]: "); + if (!uniform) { return false; } - uniforms.push_back(uniform); + uniforms.emplace_back(std::move(uniform)); } vertex_name = value["vertex"].GetString(); @@ -127,8 +126,14 @@ bool effect_reader::get_base_effect_data(const Value& value, std::string &prefix return true; } -bool effect_reader::get_shader_data(const Value& value, osd_options &options, shader_manager &shaders, std::string &vertex_name, bgfx::ShaderHandle &vertex_shader, - std::string &fragment_name, bgfx::ShaderHandle &fragment_shader) +bool effect_reader::get_shader_data( + const Value &value, + const osd_options &options, + shader_manager &shaders, + std::string &vertex_name, + bgfx::ShaderHandle &vertex_shader, + std::string &fragment_name, + bgfx::ShaderHandle &fragment_shader) { vertex_shader = shaders.load_shader(options, vertex_name); if (vertex_shader.idx == bgfx::kInvalidHandle) @@ -145,25 +150,17 @@ bool effect_reader::get_shader_data(const Value& value, osd_options &options, sh return true; } -void effect_reader::clear_uniform_list(std::vector<bgfx_uniform *> &uniforms) -{ - for (bgfx_uniform *uniform : uniforms) - { - delete uniform; - } -} - -bool effect_reader::validate_parameters(const Value& value, std::string prefix) +bool effect_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("depth"), (prefix + "Must have object value 'depth' (what are our Z-buffer settings?)\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("cull"), (prefix + "Must have object value 'cull' (do we cull triangles based on winding?)\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("write"), (prefix + "Must have object value 'write' (what are our color buffer write settings?)\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("vertex"), (prefix + "Must have string value 'vertex' (what is our vertex shader?)\n").c_str())) return false; - if (!READER_CHECK(value["vertex"].IsString(), (prefix + "Value 'vertex' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("fragment") || value.HasMember("pixel"), (prefix + "Must have string value named 'fragment' or 'pixel' (what is our fragment/pixel shader?)\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("fragment") || value["fragment"].IsString(), (prefix + "Value 'fragment' must be a string\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("pixel") || value["pixel"].IsString(), (prefix + "Value 'pixel' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("uniforms"), (prefix + "Must have array value 'uniforms' (what are our shader's parameters?)\n").c_str())) return false; - if (!READER_CHECK(value["uniforms"].IsArray(), (prefix + "Value 'uniforms' must be an array\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("depth"), "%sMust have object value 'depth' (what are our Z-buffer settings?)\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("cull"), "%sMust have object value 'cull' (do we cull triangles based on winding?)\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("write"), "%sMust have object value 'write' (what are our color buffer write settings?)\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("vertex"), "%sMust have string value 'vertex' (what is our vertex shader?)\n", prefix)) return false; + if (!READER_CHECK(value["vertex"].IsString(), "%sValue 'vertex' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("fragment") || value.HasMember("pixel"), "%sMust have string value named 'fragment' or 'pixel' (what is our fragment/pixel shader?)\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("fragment") || value["fragment"].IsString(), "%sValue 'fragment' must be a string\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("pixel") || value["pixel"].IsString(), "%sValue 'pixel' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("uniforms"), "%sMust have array value 'uniforms' (what are our shader's parameters?)\n", prefix)) return false; + if (!READER_CHECK(value["uniforms"].IsArray(), "%sValue 'uniforms' must be an array\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/effectreader.h b/src/osd/modules/render/bgfx/effectreader.h index 095f06a56ea..db590e5eeb3 100644 --- a/src/osd/modules/render/bgfx/effectreader.h +++ b/src/osd/modules/render/bgfx/effectreader.h @@ -6,36 +6,38 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_EFFECTREADER_H +#define MAME_RENDER_BGFX_EFFECTREADER_H -#ifndef __DRAWBGFX_EFFECT_READER__ -#define __DRAWBGFX_EFFECT_READER__ +#pragma once #include "statereader.h" #include <bgfx/bgfx.h> +#include <memory> #include <string> #include <vector> + class bgfx_effect; class bgfx_uniform; class osd_options; class shader_manager; + class effect_reader : public state_reader { public: - static bgfx_effect *read_from_value(const Value& value, std::string prefix, osd_options &options, shader_manager& shaders); - static bool validate_value(const Value& value, std::string prefix, osd_options &options); + static std::unique_ptr<bgfx_effect> read_from_value(const std::string &name, const Value& value, const std::string &prefix, const osd_options &options, shader_manager& shaders); + static bool validate_value(const Value& value, const std::string &prefix, const osd_options &options); private: - static bool get_base_effect_data(const Value& value, std::string &prefix, uint64_t &flags, std::string &vertex_name, std::string &fragment_name, - std::vector<bgfx_uniform *> &uniforms); - static bool get_shader_data(const Value& value, osd_options &options, shader_manager &shaders, std::string &vertex_name, bgfx::ShaderHandle &vertex_shader, + static bool get_base_effect_data(const Value& value, const std::string &prefix, uint64_t &flags, std::string &vertex_name, std::string &fragment_name, + std::vector<std::unique_ptr<bgfx_uniform> > &uniforms); + static bool get_shader_data(const Value& value, const osd_options &options, shader_manager &shaders, std::string &vertex_name, bgfx::ShaderHandle &vertex_shader, std::string &fragment_name, bgfx::ShaderHandle &fragment_shader); - static void clear_uniform_list(std::vector<bgfx_uniform *> &uniforms); - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_EFFECT_READER__ +#endif // MAME_RENDER_BGFX_EFFECTREADER_H diff --git a/src/osd/modules/render/bgfx/entryuniform.h b/src/osd/modules/render/bgfx/entryuniform.h index 9c1462f360f..88ccc3e536d 100644 --- a/src/osd/modules/render/bgfx/entryuniform.h +++ b/src/osd/modules/render/bgfx/entryuniform.h @@ -9,15 +9,15 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_ENTRYUNIFORM_H +#define MAME_RENDER_BGFX_ENTRYUNIFORM_H + #pragma once -#ifndef DRAWBGFX_ENTRY_UNIFORM -#define DRAWBGFX_ENTRY_UNIFORM +#include "uniform.h" #include <bgfx/bgfx.h> -#include "uniform.h" - class bgfx_entry_uniform { public: @@ -25,10 +25,10 @@ public: virtual ~bgfx_entry_uniform() { } virtual void bind() = 0; - std::string name() const { return m_uniform->name(); } + const std::string &name() const { return m_uniform->name(); } protected: bgfx_uniform* m_uniform; }; -#endif // __DRAWBGFX_ENTRY_UNIFORM__ +#endif // MAME_RENDER_BGFX_ENTRYUNIFORM_H diff --git a/src/osd/modules/render/bgfx/entryuniformreader.cpp b/src/osd/modules/render/bgfx/entryuniformreader.cpp index 947f4c56e90..62c618f39a7 100644 --- a/src/osd/modules/render/bgfx/entryuniformreader.cpp +++ b/src/osd/modules/render/bgfx/entryuniformreader.cpp @@ -15,7 +15,7 @@ #include "paramuniformreader.h" #include "uniform.h" -bgfx_entry_uniform* entry_uniform_reader::read_from_value(const Value& value, std::string prefix, bgfx_effect* effect, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params) +bgfx_entry_uniform* entry_uniform_reader::read_from_value(const Value& value, const std::string &prefix, bgfx_effect* effect, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params) { if (!validate_parameters(value, prefix)) { @@ -25,7 +25,7 @@ bgfx_entry_uniform* entry_uniform_reader::read_from_value(const Value& value, st std::string name = value["uniform"].GetString(); bgfx_uniform* uniform = effect->uniform(name); - if (!READER_CHECK(uniform != nullptr, (prefix + "Uniform '" + name + " does not appear to exist\n").c_str())) + if (!READER_CHECK(uniform != nullptr, "%sUniform '%s' does not appear to exist\n", prefix, name)) { return nullptr; } @@ -44,16 +44,16 @@ bgfx_entry_uniform* entry_uniform_reader::read_from_value(const Value& value, st } else { - READER_CHECK(false, (prefix + "Unrecognized uniform type for uniform binding " + name).c_str()); + READER_CHECK(false, "%sUnrecognized uniform type for uniform binding %s", prefix, name); } return nullptr; } -bool entry_uniform_reader::validate_parameters(const Value& value, std::string prefix) +bool entry_uniform_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("uniform"), (prefix + "Must have string value 'uniform' (what uniform are we mapping?)\n").c_str())) return false; - if (!READER_CHECK(value["uniform"].IsString(), (prefix + "Value 'effect' must be a string\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("uniform"), "%sMust have string value 'uniform' (what uniform are we mapping?)\n", prefix)) return false; + if (!READER_CHECK(value["uniform"].IsString(), "%sValue 'effect' must be a string\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/entryuniformreader.h b/src/osd/modules/render/bgfx/entryuniformreader.h index 73c66269a5e..c85a2647826 100644 --- a/src/osd/modules/render/bgfx/entryuniformreader.h +++ b/src/osd/modules/render/bgfx/entryuniformreader.h @@ -6,16 +6,16 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_ENTRYUNIFORMREADER_H +#define MAME_RENDER_BGFX_ENTRYUNIFORMREADER_H + #pragma once -#ifndef __DRAWBGFX_ENTRY_UNIFORM_READER__ -#define __DRAWBGFX_ENTRY_UNIFORM_READER__ +#include "statereader.h" #include <string> #include <map> -#include "statereader.h" - class bgfx_entry_uniform; class bgfx_slider; class bgfx_effect; @@ -24,10 +24,10 @@ class bgfx_parameter; class entry_uniform_reader : public state_reader { public: - static bgfx_entry_uniform* read_from_value(const Value& value, std::string prefix, bgfx_effect* effect, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params); + static bgfx_entry_uniform* read_from_value(const Value& value, const std::string &prefix, bgfx_effect* effect, std::map<std::string, bgfx_slider*>& sliders, std::map<std::string, bgfx_parameter*>& params); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_ENTRY_UNIFORM_READER__ +#endif // MAME_RENDER_BGFX_ENTRYUNIFORMREADER_H diff --git a/src/osd/modules/render/bgfx/frameparameter.cpp b/src/osd/modules/render/bgfx/frameparameter.cpp index 841d4d0d2e5..6b16fe1d5fa 100644 --- a/src/osd/modules/render/bgfx/frameparameter.cpp +++ b/src/osd/modules/render/bgfx/frameparameter.cpp @@ -8,8 +8,10 @@ #include "frameparameter.h" -bgfx_frame_parameter::bgfx_frame_parameter(std::string name, parameter_type type, uint32_t period) - : bgfx_parameter(name, type) +#include <utility> + +bgfx_frame_parameter::bgfx_frame_parameter(std::string &&name, parameter_type type, uint32_t period) + : bgfx_parameter(std::move(name), type) , m_current_frame(0) , m_period(period) { @@ -20,7 +22,7 @@ float bgfx_frame_parameter::value() return float(m_current_frame); } -void bgfx_frame_parameter::tick(double /*delta*/) +void bgfx_frame_parameter::tick(double delta) { m_current_frame++; m_current_frame %= m_period; diff --git a/src/osd/modules/render/bgfx/frameparameter.h b/src/osd/modules/render/bgfx/frameparameter.h index e559d13c1d8..2a1e8f6094b 100644 --- a/src/osd/modules/render/bgfx/frameparameter.h +++ b/src/osd/modules/render/bgfx/frameparameter.h @@ -6,22 +6,20 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_FRAMEPARAMETER_H +#define MAME_RENDER_BGFX_FRAMEPARAMETER_H -#ifndef __DRAWBGFX_FRAME_PARAMETER__ -#define __DRAWBGFX_FRAME_PARAMETER__ +#pragma once -#include <bgfx/bgfx.h> +#include "parameter.h" +#include <cstdint> #include <string> -#include "parameter.h" - class bgfx_frame_parameter : public bgfx_parameter { public: - bgfx_frame_parameter(std::string name, parameter_type type, uint32_t period); - virtual ~bgfx_frame_parameter() { } + bgfx_frame_parameter(std::string &&name, parameter_type type, uint32_t period); virtual float value() override; virtual void tick(double delta) override; @@ -31,4 +29,4 @@ private: uint32_t m_period; }; -#endif // __DRAWBGFX_FRAME_PARAMETER__ +#endif // MAME_RENDER_BGFX_FRAMEPARAMETER_H diff --git a/src/osd/modules/render/bgfx/inputpair.cpp b/src/osd/modules/render/bgfx/inputpair.cpp index 5f1324c2581..154252db04f 100644 --- a/src/osd/modules/render/bgfx/inputpair.cpp +++ b/src/osd/modules/render/bgfx/inputpair.cpp @@ -11,15 +11,18 @@ #include "inputpair.h" -#include "../frontend/mame/ui/slider.h" - -#include "texture.h" -#include "target.h" -#include "effect.h" -#include "uniform.h" #include "chainmanager.h" +#include "effect.h" #include "slider.h" #include "sliderdirtynotifier.h" +#include "target.h" +#include "texture.h" +#include "uniform.h" + +#include "../frontend/mame/ui/slider.h" + +#include "util/strformat.h" + bgfx_input_pair::bgfx_input_pair(int index, std::string sampler, std::string texture, std::vector<std::string> available_textures, std::string selection, chain_manager& chains, uint32_t screen_index) : m_index(index) @@ -53,20 +56,31 @@ void bgfx_input_pair::bind(bgfx_effect *effect, const int32_t screen) const if (!provider) return; + float rowpixels(provider->rowpixels()); + float width_margin(provider->width_margin()); + float height(provider->height()); + bgfx_uniform *tex_size = effect->uniform("u_tex_size" + std::to_string(m_index)); if (tex_size && provider) { - float values[2] = { float(provider->rowpixels()), float(provider->height()) }; + float values[2] = { rowpixels, height }; tex_size->set(values, sizeof(float) * 2); } bgfx_uniform *inv_tex_size = effect->uniform("u_inv_tex_size" + std::to_string(m_index)); if (inv_tex_size && provider) { - float values[2] = { 1.0f / float(provider->rowpixels()), 1.0f / float(provider->height()) }; + float values[2] = { 1.0f / rowpixels, 1.0f / height }; inv_tex_size->set(values, sizeof(float) * 2); } + bgfx_uniform *tex_bounds = effect->uniform("u_tex_bounds" + std::to_string(m_index)); + if (tex_bounds && provider) + { + float values[4] = { width_margin / rowpixels, 0.0f, 1.0f + width_margin / rowpixels, 1.0f }; + tex_bounds->set(values, sizeof(float) * 4); + } + bgfx::setTexture(m_index, effect->uniform(m_sampler)->handle(), chains().textures().handle(name)); } @@ -109,11 +123,11 @@ void bgfx_input_pair::create_selection_slider(uint32_t screen_index) int32_t maxval = m_available_textures.size() - 1; int32_t incval = 1; - std::string description = "Window " + std::to_string(chains().window_index()) + ", Screen " + std::to_string(screen_index) + " " + m_selection + ":"; - using namespace std::placeholders; - m_slider_state = std::make_unique<slider_state>(std::move(description), minval, defval, maxval, incval, - std::bind(&bgfx_input_pair::texture_changed, this, screen_index, _1, _2)); + m_slider_state = std::make_unique<slider_state>( + util::string_format("Window %1$u, Screen %1$u %3$s", chains().window_index(), screen_index, m_selection), + minval, defval, maxval, incval, + std::bind(&bgfx_input_pair::texture_changed, this, screen_index, _1, _2)); ui::menu_item item(ui::menu_item_type::SLIDER, m_slider_state.get()); item.set_text(m_slider_state->description); @@ -130,9 +144,7 @@ std::vector<ui::menu_item> bgfx_input_pair::get_slider_list() std::vector<ui::menu_item> sliders; if (!needs_sliders()) - { return sliders; - } sliders.push_back(m_selection_slider); diff --git a/src/osd/modules/render/bgfx/parameter.h b/src/osd/modules/render/bgfx/parameter.h index 2df18365a4c..baa2ff017ad 100644 --- a/src/osd/modules/render/bgfx/parameter.h +++ b/src/osd/modules/render/bgfx/parameter.h @@ -10,12 +10,13 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_PARAMETER_H +#define MAME_RENDER_BGFX_PARAMETER_H -#ifndef __DRAWBGFX_PARAMETER__ -#define __DRAWBGFX_PARAMETER__ +#pragma once #include <string> +#include <utility> class bgfx_parameter { @@ -27,18 +28,18 @@ public: PARAM_TIME }; - bgfx_parameter(std::string name, parameter_type type) : m_name(name), m_type(type) { } - virtual ~bgfx_parameter() { } + bgfx_parameter(std::string &&name, parameter_type type) : m_name(std::move(name)), m_type(type) { } + virtual ~bgfx_parameter() = default; virtual void tick(double delta) = 0; // Getters virtual float value() = 0; - std::string name() const { return m_name; } + const std::string &name() const { return m_name; } protected: std::string m_name; parameter_type m_type; }; -#endif // __DRAWBGFX_PARAMETER__ +#endif // MAME_RENDER_BGFX_PARAMETER_H diff --git a/src/osd/modules/render/bgfx/paramreader.cpp b/src/osd/modules/render/bgfx/paramreader.cpp index 1b14b2aa56a..0eeb5f22e84 100644 --- a/src/osd/modules/render/bgfx/paramreader.cpp +++ b/src/osd/modules/render/bgfx/paramreader.cpp @@ -6,15 +6,13 @@ // //============================================================ -#include <string> - #include "paramreader.h" -#include "parameter.h" +#include "chainmanager.h" #include "frameparameter.h" -#include "windowparameter.h" +#include "parameter.h" #include "timeparameter.h" -#include "chainmanager.h" +#include "windowparameter.h" const parameter_reader::string_to_enum parameter_reader::TYPE_NAMES[parameter_reader::TYPE_COUNT] = { { "frame", bgfx_parameter::parameter_type::PARAM_FRAME }, @@ -22,7 +20,7 @@ const parameter_reader::string_to_enum parameter_reader::TYPE_NAMES[parameter_re { "time", bgfx_parameter::parameter_type::PARAM_TIME } }; -bgfx_parameter* parameter_reader::read_from_value(const Value& value, std::string prefix, chain_manager& chains) +bgfx_parameter* parameter_reader::read_from_value(const Value& value, const std::string &prefix, chain_manager& chains) { if (!validate_parameters(value, prefix)) { @@ -35,32 +33,32 @@ bgfx_parameter* parameter_reader::read_from_value(const Value& value, std::strin if (type == bgfx_parameter::parameter_type::PARAM_FRAME) { uint32_t period = int(value["period"].GetDouble()); - return new bgfx_frame_parameter(name, type, period); + return new bgfx_frame_parameter(std::move(name), type, period); } else if (type == bgfx_parameter::parameter_type::PARAM_WINDOW) { - return new bgfx_window_parameter(name, type, chains.window_index()); + return new bgfx_window_parameter(std::move(name), type, chains.window_index()); } else if (type == bgfx_parameter::parameter_type::PARAM_TIME) { auto limit = float(value["limit"].GetDouble()); - return new bgfx_time_parameter(name, type, limit); + return new bgfx_time_parameter(std::move(name), type, limit); } else { - READER_CHECK(false, (prefix + "Unknown parameter type '" + std::string(value["type"].GetString()) + "'\n").c_str()); + READER_CHECK(false, "%sUnknown parameter type '%s'\n", prefix, value["type"].GetString()); } return nullptr; } -bool parameter_reader::validate_parameters(const Value& value, std::string prefix) +bool parameter_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'name'\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("type"), (prefix + "Must have string value 'type'\n").c_str())) return false; - if (!READER_CHECK(value["type"].IsString(), (prefix + "Value 'type' must be a string\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("period") || value["period"].IsNumber(), (prefix + "Value 'period' must be numeric\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("limit") || value["limit"].IsNumber(), (prefix + "Value 'period' must be numeric\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("name"), "%sMust have string value 'name'\n", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%sValue 'name' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("type"), "%sMust have string value 'type'\n", prefix)) return false; + if (!READER_CHECK(value["type"].IsString(), "%sValue 'type' must be a string\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("period") || value["period"].IsNumber(), "%sValue 'period' must be numeric\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("limit") || value["limit"].IsNumber(), "%sValue 'period' must be numeric\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/paramreader.h b/src/osd/modules/render/bgfx/paramreader.h index 013d6589cc3..bfd23b14a42 100644 --- a/src/osd/modules/render/bgfx/paramreader.h +++ b/src/osd/modules/render/bgfx/paramreader.h @@ -6,26 +6,28 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_PARAMREADER_H +#define MAME_RENDER_BGFX_PARAMREADER_H -#ifndef __DRAWBGFX_PARAM_READER__ -#define __DRAWBGFX_PARAM_READER__ +#pragma once #include "statereader.h" +#include <string> + class bgfx_parameter; class chain_manager; class parameter_reader : public state_reader { public: - static bgfx_parameter* read_from_value(const Value& value, std::string prefix, chain_manager& chains); + static bgfx_parameter* read_from_value(const Value& value, const std::string &prefix, chain_manager& chains); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); static const int TYPE_COUNT = 3; static const string_to_enum TYPE_NAMES[TYPE_COUNT]; }; -#endif // __DRAWBGFX_PARAM_READER__ +#endif // MAME_RENDER_BGFX_PARAMREADER_H diff --git a/src/osd/modules/render/bgfx/paramuniform.h b/src/osd/modules/render/bgfx/paramuniform.h index b49fb2c7d61..5f5b69c96a6 100644 --- a/src/osd/modules/render/bgfx/paramuniform.h +++ b/src/osd/modules/render/bgfx/paramuniform.h @@ -6,10 +6,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_PARAMUNIFORM_H +#define MAME_RENDER_BGFX_PARAMUNIFORM_H -#ifndef __DRAWBGFX_PARAM_UNIFORM__ -#define __DRAWBGFX_PARAM_UNIFORM__ +#pragma once #include "entryuniform.h" @@ -26,4 +26,4 @@ private: bgfx_parameter* m_param; }; -#endif // __DRAWBGFX_PARAM_UNIFORM__ +#endif // MAME_RENDER_BGFX_PARAMUNIFORM_H diff --git a/src/osd/modules/render/bgfx/paramuniformreader.cpp b/src/osd/modules/render/bgfx/paramuniformreader.cpp index 2b613fc216c..8c5cd265195 100644 --- a/src/osd/modules/render/bgfx/paramuniformreader.cpp +++ b/src/osd/modules/render/bgfx/paramuniformreader.cpp @@ -9,10 +9,10 @@ #include "paramuniformreader.h" #include "entryuniform.h" -#include "paramuniform.h" #include "parameter.h" +#include "paramuniform.h" -bgfx_entry_uniform* param_uniform_reader::read_from_value(const Value& value, std::string prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_parameter*>& params) +bgfx_entry_uniform* param_uniform_reader::read_from_value(const Value& value, const std::string &prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_parameter*>& params) { if (!validate_parameters(value, prefix)) { @@ -24,9 +24,9 @@ bgfx_entry_uniform* param_uniform_reader::read_from_value(const Value& value, st return new bgfx_param_uniform(uniform, params[parameter]); } -bool param_uniform_reader::validate_parameters(const Value& value, std::string prefix) +bool param_uniform_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("parameter"), (prefix + "Must have string value 'parameter' (what parameter is being mapped?)\n").c_str())) return false; - if (!READER_CHECK(value["parameter"].IsString(), (prefix + "Value 'parameter' must be a string\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("parameter"), "%sMust have string value 'parameter' (what parameter is being mapped?)\n", prefix)) return false; + if (!READER_CHECK(value["parameter"].IsString(), "%sValue 'parameter' must be a string\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/paramuniformreader.h b/src/osd/modules/render/bgfx/paramuniformreader.h index baf281b562b..b1ed2a87f69 100644 --- a/src/osd/modules/render/bgfx/paramuniformreader.h +++ b/src/osd/modules/render/bgfx/paramuniformreader.h @@ -6,15 +6,15 @@ // //================================================================== +#ifndef MAME_RENDER_BGFX_PARAMUNIFORMREADER_H +#define MAME_RENDER_BGFX_PARAMUNIFORMREADER_H + #pragma once -#ifndef __DRAWBGFX_PARAM_UNIFORM_READER__ -#define __DRAWBGFX_PARAM_UNIFORM_READER__ +#include "statereader.h" -#include <string> #include <map> - -#include "statereader.h" +#include <string> class bgfx_entry_uniform; class bgfx_uniform; @@ -23,10 +23,10 @@ class bgfx_parameter; class param_uniform_reader : public state_reader { public: - static bgfx_entry_uniform* read_from_value(const Value& value, std::string prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_parameter*>& params); + static bgfx_entry_uniform* read_from_value(const Value& value, const std::string &prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_parameter*>& params); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_PARAM_UNIFORM_READER__ +#endif // MAME_RENDER_BGFX_PARAMUNIFORMREADER_H diff --git a/src/osd/modules/render/bgfx/shadermanager.cpp b/src/osd/modules/render/bgfx/shadermanager.cpp index dd894426794..763a744e4aa 100644 --- a/src/osd/modules/render/bgfx/shadermanager.cpp +++ b/src/osd/modules/render/bgfx/shadermanager.cpp @@ -16,11 +16,10 @@ #include "osdfile.h" #include "modules/lib/osdobj_common.h" +#include <bx/file.h> #include <bx/math.h> #include <bx/readerwriter.h> -#include <bx/file.h> -#include <bgfx/bgfx.h> shader_manager::~shader_manager() { @@ -31,7 +30,7 @@ shader_manager::~shader_manager() m_shaders.clear(); } -bgfx::ShaderHandle shader_manager::get_or_load_shader(osd_options &options, std::string name) +bgfx::ShaderHandle shader_manager::get_or_load_shader(const osd_options &options, const std::string &name) { std::map<std::string, bgfx::ShaderHandle>::iterator iter = m_shaders.find(name); if (iter != m_shaders.end()) @@ -48,7 +47,7 @@ bgfx::ShaderHandle shader_manager::get_or_load_shader(osd_options &options, std: return handle; } -bgfx::ShaderHandle shader_manager::load_shader(osd_options &options, std::string name) +bgfx::ShaderHandle shader_manager::load_shader(const osd_options &options, const std::string &name) { std::string shader_path = make_path_string(options, name); const bgfx::Memory* mem = load_mem(shader_path + name + ".bin"); @@ -60,16 +59,17 @@ bgfx::ShaderHandle shader_manager::load_shader(osd_options &options, std::string return BGFX_INVALID_HANDLE; } -bool shader_manager::is_shader_present(osd_options &options, std::string name) +bool shader_manager::is_shader_present(const osd_options &options, const std::string &name) { std::string shader_path = make_path_string(options, name); std::string file_name = shader_path + name + ".bin"; bx::FileReader reader; + bx::ErrorAssert err; if (bx::open(&reader, file_name.c_str())) { uint32_t expected_size(bx::getSize(&reader)); uint8_t *data = new uint8_t[expected_size]; - uint32_t read_size = (uint32_t)bx::read(&reader, data, expected_size); + uint32_t read_size = (uint32_t)bx::read(&reader, data, expected_size, &err); delete [] data; bx::close(&reader); @@ -79,7 +79,7 @@ bool shader_manager::is_shader_present(osd_options &options, std::string name) return false; } -std::string shader_manager::make_path_string(osd_options &options, std::string name) +std::string shader_manager::make_path_string(const osd_options &options, const std::string &name) { std::string shader_path(options.bgfx_path()); shader_path += PATH_SEPARATOR "shaders" PATH_SEPARATOR; @@ -119,17 +119,18 @@ std::string shader_manager::make_path_string(osd_options &options, std::string n fatalerror("Unknown BGFX renderer type %d", bgfx::getRendererType()); } shader_path += PATH_SEPARATOR; - return osd_subst_env(shader_path); + return shader_path; } -const bgfx::Memory* shader_manager::load_mem(std::string name) +const bgfx::Memory* shader_manager::load_mem(const std::string &name) { bx::FileReader reader; if (bx::open(&reader, name.c_str())) { + bx::ErrorAssert err; uint32_t size(bx::getSize(&reader)); const bgfx::Memory* mem = bgfx::alloc(size + 1); - bx::read(&reader, mem->data, size); + bx::read(&reader, mem->data, size, &err); bx::close(&reader); mem->data[mem->size - 1] = '\0'; @@ -137,7 +138,7 @@ const bgfx::Memory* shader_manager::load_mem(std::string name) } else { - osd_printf_error("Unable to load shader %s\n", name.c_str()); + osd_printf_error("Unable to load shader %s\n", name); } return nullptr; } diff --git a/src/osd/modules/render/bgfx/shadermanager.h b/src/osd/modules/render/bgfx/shadermanager.h index 81fa4260f47..12ac739d35a 100644 --- a/src/osd/modules/render/bgfx/shadermanager.h +++ b/src/osd/modules/render/bgfx/shadermanager.h @@ -9,10 +9,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_SHADERMANAGER_H +#define MAME_RENDER_BGFX_SHADERMANAGER_H -#ifndef __DRAWBGFX_SHADER_MANAGER__ -#define __DRAWBGFX_SHADER_MANAGER__ +#pragma once #include <bgfx/bgfx.h> @@ -22,21 +22,23 @@ class osd_options; -class shader_manager { + +class shader_manager +{ public: shader_manager() { } ~shader_manager(); // Getters - bgfx::ShaderHandle get_or_load_shader(osd_options &options, std::string name); - static bgfx::ShaderHandle load_shader(osd_options &options, std::string name); - static bool is_shader_present(osd_options &options, std::string name); + bgfx::ShaderHandle get_or_load_shader(const osd_options &options, const std::string &name); + static bgfx::ShaderHandle load_shader(const osd_options &options, const std::string &name); + static bool is_shader_present(const osd_options &options, const std::string &name); private: - static std::string make_path_string(osd_options &options, std::string name); - static const bgfx::Memory* load_mem(std::string name); + static std::string make_path_string(const osd_options &options, const std::string &name); + static const bgfx::Memory* load_mem(const std::string &name); - std::map<std::string, bgfx::ShaderHandle> m_shaders; + std::map<std::string, bgfx::ShaderHandle> m_shaders; }; -#endif // __DRAWBGFX_SHADER_MANAGER__ +#endif // MAME_RENDER_BGFX_SHADERMANAGER_H diff --git a/src/osd/modules/render/bgfx/shaders/chains/blurs/fs_smart-blur.sc b/src/osd/modules/render/bgfx/shaders/chains/blurs/fs_smart-blur.sc index 4c688c4d6f4..6ea70cb6aac 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/blurs/fs_smart-blur.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/blurs/fs_smart-blur.sc @@ -39,26 +39,26 @@ uniform vec4 SB_THRESHOLD; bool eq(vec3 c1, vec3 c2) { - vec3 df = abs(c1 - c2); - return df.r < SB_THRESHOLD.r && df.g < SB_THRESHOLD.g && df.b < SB_THRESHOLD.b; + vec3 df = abs(c1 - c2); + return df.r < SB_THRESHOLD.r && df.g < SB_THRESHOLD.g && df.b < SB_THRESHOLD.b; } void main() { - vec3 A = texture2D(s_p, v_texcoord1.xw).xyz; - vec3 B = texture2D(s_p, v_texcoord1.yw).xyz; - vec3 C = texture2D(s_p, v_texcoord1.zw).xyz; - vec3 D = texture2D(s_p, v_texcoord2.xw).xyz; - vec3 E = texture2D(s_p, v_texcoord2.yw).xyz; - vec3 F = texture2D(s_p, v_texcoord2.zw).xyz; - vec3 G = texture2D(s_p, v_texcoord3.xw).xyz; - vec3 H = texture2D(s_p, v_texcoord3.yw).xyz; - vec3 I = texture2D(s_p, v_texcoord3.zw).xyz; + vec3 A = texture2D(s_p, v_texcoord1.xw).xyz; + vec3 B = texture2D(s_p, v_texcoord1.yw).xyz; + vec3 C = texture2D(s_p, v_texcoord1.zw).xyz; + vec3 D = texture2D(s_p, v_texcoord2.xw).xyz; + vec3 E = texture2D(s_p, v_texcoord2.yw).xyz; + vec3 F = texture2D(s_p, v_texcoord2.zw).xyz; + vec3 G = texture2D(s_p, v_texcoord3.xw).xyz; + vec3 H = texture2D(s_p, v_texcoord3.yw).xyz; + vec3 I = texture2D(s_p, v_texcoord3.zw).xyz; - if (eq(E,F) && eq(E,H) && eq(E,I) && eq(E,B) && eq(E,C) && eq(E,A) && eq(E,D) && eq(E,G)) - { - E = (A + B + C + D + E + F + G + H + I) / 9.0; - } + if (eq(E,F) && eq(E,H) && eq(E,I) && eq(E,B) && eq(E,C) && eq(E,A) && eq(E,D) && eq(E,G)) + { + E = (A + B + C + D + E + F + G + H + I) / 9.0; + } - gl_FragColor = vec4(E, 1.0); + gl_FragColor = vec4(E, 1.0); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/blurs/vs_smart-blur.sc b/src/osd/modules/render/bgfx/shaders/chains/blurs/vs_smart-blur.sc index 1b967f894d4..40b0947da38 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/blurs/vs_smart-blur.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/blurs/vs_smart-blur.sc @@ -32,10 +32,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/crt-geom_common.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/crt-geom_common.sc index 6863a180ef6..9118e42d5c8 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/crt-geom_common.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/crt-geom_common.sc @@ -13,12 +13,12 @@ vec4 TEX2D(vec2 c) { - vec2 underscan = step(0.0,c) * step(0.0,vec2_splat(1.0)-c); - vec4 col = texture2D(mpass_texture, c) * vec4_splat(underscan.x*underscan.y); + vec2 underscan = step(0.0,c) * step(0.0,vec2_splat(1.0)-c); + vec4 col = texture2D(mpass_texture, c) * vec4_splat(underscan.x*underscan.y); #ifdef LINEAR_PROCESSING - col = pow(col, vec4_splat(CRTgamma.x)); + col = pow(col, vec4_splat(CRTgamma.x)); #endif - return col; + return col; } // Enable screen curvature. @@ -48,43 +48,43 @@ uniform vec4 cornersmooth; float intersect(vec2 xy , vec2 sinangle, vec2 cosangle) { - float A = dot(xy,xy)+d.x*d.x; - float B = 2.0*(R.x*(dot(xy,sinangle)-d.x*cosangle.x*cosangle.y)-d.x*d.x); - float C = d.x*d.x + 2.0*R.x*d.x*cosangle.x*cosangle.y; - return (-B-sqrt(B*B-4.0*A*C))/(2.0*A); + float A = dot(xy,xy)+d.x*d.x; + float B = 2.0*(R.x*(dot(xy,sinangle)-d.x*cosangle.x*cosangle.y)-d.x*d.x); + float C = d.x*d.x + 2.0*R.x*d.x*cosangle.x*cosangle.y; + return (-B-sqrt(B*B-4.0*A*C))/(2.0*A); } vec2 bkwtrans(vec2 xy, vec2 sinangle, vec2 cosangle) { - float c = intersect(xy, sinangle, cosangle); - vec2 pt = vec2_splat(c)*xy; - pt -= vec2_splat(-R.x)*sinangle; - pt /= vec2_splat(R.x); - vec2 tang = sinangle/cosangle; - vec2 poc = pt/cosangle; - float A = dot(tang,tang)+1.0; - float B = -2.0*dot(poc,tang); - float C = dot(poc,poc)-1.0; - float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A); - vec2 uv = (pt-a*sinangle)/cosangle; - float r = FIX(R.x*acos(a)); - return uv*r/sin(r/R.x); + float c = intersect(xy, sinangle, cosangle); + vec2 pt = vec2_splat(c)*xy; + pt -= vec2_splat(-R.x)*sinangle; + pt /= vec2_splat(R.x); + vec2 tang = sinangle/cosangle; + vec2 poc = pt/cosangle; + float A = dot(tang,tang)+1.0; + float B = -2.0*dot(poc,tang); + float C = dot(poc,poc)-1.0; + float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A); + vec2 uv = (pt-a*sinangle)/cosangle; + float r = FIX(R.x*acos(a)); + return uv*r/sin(r/R.x); } vec2 transform(vec2 coord, vec3 stretch, vec2 sinangle, vec2 cosangle) { - coord = (coord-vec2_splat(0.5))*aspect.xy*stretch.z+stretch.xy; - return (bkwtrans(coord, sinangle, cosangle)/overscan.xy/aspect.xy+vec2_splat(0.5)); + coord = (coord-vec2_splat(0.5))*aspect.xy*stretch.z+stretch.xy; + return (bkwtrans(coord, sinangle, cosangle)/overscan.xy/aspect.xy+vec2_splat(0.5)); } float corner(vec2 coord) { - coord = (coord - vec2_splat(0.5)) * overscan.xy + vec2_splat(0.5); - coord = min(coord, vec2_splat(1.0)-coord) * aspect.xy; - vec2 cdist = vec2_splat(cornersize.x); - coord = (cdist - min(coord,cdist)); - float dist = sqrt(dot(coord,coord)); - return clamp((max(cdist.x,1e-3)-dist)*cornersmooth.x,0.0, 1.0); + coord = (coord - vec2_splat(0.5)) * overscan.xy + vec2_splat(0.5); + coord = min(coord, vec2_splat(1.0)-coord) * aspect.xy; + vec2 cdist = vec2_splat(cornersize.x); + coord = (cdist - min(coord,cdist)); + float dist = sqrt(dot(coord,coord)); + return clamp((max(cdist.x,1e-3)-dist)*cornersmooth.x,0.0, 1.0); } // Calculate the influence of a scanline on the current pixel. @@ -95,109 +95,109 @@ float corner(vec2 coord) // the current pixel. vec4 scanlineWeights(float distance, vec4 color) { - // "wid" controls the width of the scanline beam, for each RGB channel - // The "weights" lines basically specify the formula that gives - // you the profile of the beam, i.e. the intensity as - // a function of distance from the vertical center of the - // scanline. In this case, it is gaussian if width=2, and - // becomes nongaussian for larger widths. Ideally this should - // be normalized so that the integral across the beam is - // independent of its width. That is, for a narrower beam - // "weights" should have a higher peak at the center of the - // scanline than for a wider beam. + // "wid" controls the width of the scanline beam, for each RGB channel + // The "weights" lines basically specify the formula that gives + // you the profile of the beam, i.e. the intensity as + // a function of distance from the vertical center of the + // scanline. In this case, it is gaussian if width=2, and + // becomes nongaussian for larger widths. Ideally this should + // be normalized so that the integral across the beam is + // independent of its width. That is, for a narrower beam + // "weights" should have a higher peak at the center of the + // scanline than for a wider beam. #ifdef USEGAUSSIAN - vec4 wid = spot_size.x + spot_growth.x * pow(color, vec4_splat(spot_growth_power.x)); - vec4 weights = vec4(distance / wid); - float maxwid = spot_size.x + spot_growth.x; - float norm = maxwid / ( 1.0 + exp(-1.0/(maxwid*maxwid)) ); - return norm * exp(-weights * weights) / wid; + vec4 wid = spot_size.x + spot_growth.x * pow(color, vec4_splat(spot_growth_power.x)); + vec4 weights = vec4(distance / wid); + float maxwid = spot_size.x + spot_growth.x; + float norm = maxwid / ( 1.0 + exp(-1.0/(maxwid*maxwid)) ); + return norm * exp(-weights * weights) / wid; #else - vec4 wid = 2.0 + 2.0 * pow(color, vec4_splat(4.0)); - vec4 weights = vec4_splat(distance / 0.3); - return 1.4 * exp(-pow(weights * inversesqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid); + vec4 wid = 2.0 + 2.0 * pow(color, vec4_splat(4.0)); + vec4 weights = vec4_splat(distance / 0.3); + return 1.4 * exp(-pow(weights * inversesqrt(0.5 * wid), wid)) / (0.6 + 0.2 * wid); #endif } vec4 cubic(vec4 x, float B, float C) { - // https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters - vec2 a = x.yz; // components in [0,1] - vec2 b = x.xw; // components in [1,2] - vec2 a2 = a*a; - vec2 b2 = b*b; - a = (2.0-1.5*B-1.0*C)*a*a2 + (-3.0+2.0*B+C)*a2 + (1.0-(1.0/3.0)*B); - b = ((-1.0/6.0)*B-C)*b*b2 + (B+5.0*C)*b2 + (-2.0*B-8.0*C)*b + ((4.0/3.0)*B+4.0*C); - return vec4(b.x,a.x,a.y,b.y); + // https://en.wikipedia.org/wiki/Mitchell%E2%80%93Netravali_filters + vec2 a = x.yz; // components in [0,1] + vec2 b = x.xw; // components in [1,2] + vec2 a2 = a*a; + vec2 b2 = b*b; + a = (2.0-1.5*B-1.0*C)*a*a2 + (-3.0+2.0*B+C)*a2 + (1.0-(1.0/3.0)*B); + b = ((-1.0/6.0)*B-C)*b*b2 + (B+5.0*C)*b2 + (-2.0*B-8.0*C)*b + ((4.0/3.0)*B+4.0*C); + return vec4(b.x,a.x,a.y,b.y); } vec4 x_coeffs(vec4 x, float pos_x) { - if (u_interp.x < 0.5) { // box - float wid = length(vec2(dFdx(pos_x),dFdy(pos_x))); - float dx = clamp((0.5 + 0.5*wid - x.y)/wid, 0.0, 1.0); - return vec4(0.0,dx,1.0-dx,0.0); - } else if (u_interp.x < 1.5) { // linear - return vec4(0.0, 1.0-x.y, 1.0-x.z, 0.0); - } else if (u_interp.x < 2.5) { // Lanczos - // Prevent division by zero. - vec4 coeffs = FIX(PI * x); - // Lanczos2 kernel. - coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs); - // Normalize. - coeffs /= dot(coeffs, vec4_splat(1.0)); - return coeffs; - } else if (u_interp.x < 3.5) { // Catmull-Rom - return cubic(x,0.0,0.5); - } else if (u_interp.x < 4.5) { // Mitchell-Netravali - return cubic(x,1.0/3.0,1.0/3.0); - } else /*if (u_interp.x < 5.5)*/ { // B-spline - return cubic(x,1.0,0.0); - } + if (u_interp.x < 0.5) { // box + float wid = length(vec2(dFdx(pos_x),dFdy(pos_x))); + float dx = clamp((0.5 + 0.5*wid - x.y)/wid, 0.0, 1.0); + return vec4(0.0,dx,1.0-dx,0.0); + } else if (u_interp.x < 1.5) { // linear + return vec4(0.0, 1.0-x.y, 1.0-x.z, 0.0); + } else if (u_interp.x < 2.5) { // Lanczos + // Prevent division by zero. + vec4 coeffs = FIX(PI * x); + // Lanczos2 kernel. + coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs); + // Normalize. + coeffs /= dot(coeffs, vec4_splat(1.0)); + return coeffs; + } else if (u_interp.x < 3.5) { // Catmull-Rom + return cubic(x,0.0,0.5); + } else if (u_interp.x < 4.5) { // Mitchell-Netravali + return cubic(x,1.0/3.0,1.0/3.0); + } else /*if (u_interp.x < 5.5)*/ { // B-spline + return cubic(x,1.0,0.0); + } } vec4 sample_scanline(vec2 xy, vec4 coeffs, float onex) { - // Calculate the effective colour of the given - // scanline at the horizontal location of the current pixel, - // using the Lanczos coefficients. - vec4 col = clamp(TEX2D(xy + vec2(-onex, 0.0))*coeffs.x + - TEX2D(xy)*coeffs.y + - TEX2D(xy +vec2(onex, 0.0))*coeffs.z + - TEX2D(xy + vec2(2.0 * onex, 0.0))*coeffs.w , 0.0, 1.0); - return col; + // Calculate the effective colour of the given + // scanline at the horizontal location of the current pixel, + // using the Lanczos coefficients. + vec4 col = clamp(TEX2D(xy + vec2(-onex, 0.0))*coeffs.x + + TEX2D(xy)*coeffs.y + + TEX2D(xy +vec2(onex, 0.0))*coeffs.z + + TEX2D(xy + vec2(2.0 * onex, 0.0))*coeffs.w , 0.0, 1.0); + return col; } vec3 apply_shadow_mask(vec2 coord, vec3 col) { - vec2 xy = coord * u_quad_dims.xy / u_tex_size1.xy; - vec4 mask = texture2D(mask_texture, xy); - // count of total bright pixels is encoded in the mask's alpha channel - float nbright = 255.0 - 255.0*mask.a; - // fraction of bright pixels in the mask - float fbright = nbright / ( u_tex_size1.x * u_tex_size1.y ); - // average darkening factor of the mask - float aperture_average = mix(1.0-aperture_strength.x*(1.0-aperture_brightboost.x), 1.0, fbright); - // colour of dark mask pixels - vec3 clow = vec3_splat(1.0-aperture_strength.x) * col + vec3_splat(aperture_strength.x*(aperture_brightboost.x)) * col * col; - float ifbright = 1.0 / fbright; - // colour of bright mask pixels - vec3 chi = vec3_splat(ifbright*aperture_average) * col - vec3_splat(ifbright - 1.0) * clow; - return mix(clow,chi,mask.rgb); // mask texture selects dark vs bright + vec2 xy = coord * u_quad_dims.xy / u_tex_size1.xy; + vec4 mask = texture2D(mask_texture, xy); + // count of total bright pixels is encoded in the mask's alpha channel + float nbright = 255.0 - 255.0*mask.a; + // fraction of bright pixels in the mask + float fbright = nbright / ( u_tex_size1.x * u_tex_size1.y ); + // average darkening factor of the mask + float aperture_average = mix(1.0-aperture_strength.x*(1.0-aperture_brightboost.x), 1.0, fbright); + // colour of dark mask pixels + vec3 clow = vec3_splat(1.0-aperture_strength.x) * col + vec3_splat(aperture_strength.x*(aperture_brightboost.x)) * col * col; + float ifbright = 1.0 / fbright; + // colour of bright mask pixels + vec3 chi = vec3_splat(ifbright*aperture_average) * col - vec3_splat(ifbright - 1.0) * clow; + return mix(clow,chi,mask.rgb); // mask texture selects dark vs bright } vec3 linear_to_sRGB(vec3 col) { - // only applies the gamma ramp; does not adjust the primaries - vec3 linear_ramp = vec3(lessThan(col, vec3_splat(0.0031308))); - vec3 clin = col * vec3_splat(12.92); - vec3 cpow = pow(col, vec3_splat(1.0/2.4)) * vec3_splat(1.055) - vec3_splat(0.055); - return mix(cpow, clin, linear_ramp); + // only applies the gamma ramp; does not adjust the primaries + vec3 linear_ramp = vec3(lessThan(col, vec3_splat(0.0031308))); + vec3 clin = col * vec3_splat(12.92); + vec3 cpow = pow(col, vec3_splat(1.0/2.4)) * vec3_splat(1.055) - vec3_splat(0.055); + return mix(cpow, clin, linear_ramp); } vec3 linear_to_output(vec3 col) { - if (monitorsRGB.x > 0.5) - return linear_to_sRGB(col); - else - return pow(col, vec3_splat(1.0 / monitorgamma.x)); + if (monitorsRGB.x > 0.5) + return linear_to_sRGB(col); + else + return pow(col, vec3_splat(1.0 / monitorgamma.x)); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom-deluxe.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom-deluxe.sc index 72705bdcb83..5b8a633a642 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom-deluxe.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom-deluxe.sc @@ -28,9 +28,9 @@ uniform vec4 rasterbloom; uniform vec4 blurwidth; -vec3 texblur(vec2 c) +vec4 texblur(vec2 c) { - vec3 col = pow(texture2D(blur_texture,c).rgb, vec3_splat(CRTgamma.x)); + vec4 col = pow(texture2D(blur_texture,c), vec4(vec3_splat(CRTgamma.x),1.0)); // taper the blur texture outside its border with a gaussian float w = blurwidth.x / 320.0; c = min(c, vec2_splat(1.0)-c) * aspect.xy * vec2_splat(1.0/w); @@ -38,7 +38,7 @@ vec3 texblur(vec2 c) // approximation of erf gives smooth step // (convolution of gaussian with step) c = (step(0.0,c)-vec2_splat(0.5)) * sqrt(vec2_splat(1.0)-e2c) * (vec2_splat(1.0) + vec2_splat(0.1749)*e2c) + vec2_splat(0.5); - return col * vec3_splat( c.x * c.y ); + return col * vec4_splat( c.x * c.y ); } void main() @@ -125,16 +125,16 @@ void main() weights_prev=weights_prev+scanlineWeights(uv_ratio.y+1.0, col_prev)/3.0; weights_next=weights_next+scanlineWeights(uv_ratio.y-1.0, col_next)/3.0; #endif - vec3 mul_res = (col * weights + col_prev * weights_prev + col_next * weights_next).rgb; + vec4 mul_res = col * weights + col_prev * weights_prev + col_next * weights_next; // halation and corners - vec3 blur = texblur(xy0); + vec4 blur = texblur(xy0); // include factor of rbloom: // (probably imperceptible) brightness reduction when raster grows - mul_res = mix(mul_res, blur, halation.x) * vec3_splat(cval*rbloom); + mul_res = mix(mul_res, blur, halation.x) * vec4_splat(cval*rbloom); // Shadow mask - vec3 cout = apply_shadow_mask(v_texCoord.xy, mul_res); + vec3 cout = apply_shadow_mask(v_texCoord.xy, mul_res.rgb); // Convert the image gamma for display on our output device. cout = linear_to_output(cout); diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom.sc index c096ebca7e0..5226a9999cc 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_crt-geom.sc @@ -24,79 +24,79 @@ uniform vec4 u_quad_dims; void main() { - // Here's a helpful diagram to keep in mind while trying to - // understand the code: - // - // | | | | | - // ------------------------------- - // | | | | | - // | 01 | 11 | 21 | 31 | <-- current scanline - // | | @ | | | - // ------------------------------- - // | | | | | - // | 02 | 12 | 22 | 32 | <-- next scanline - // | | | | | - // ------------------------------- - // | | | | | - // - // Each character-cell represents a pixel on the output - // surface, "@" represents the current pixel (always somewhere - // in the bottom half of the current scan-line, or the top-half - // of the next scanline). The grid of lines represents the - // edges of the texels of the underlying texture. - - // Texture coordinates of the texel containing the active pixel. - vec2 xy; - if (curvature.x > 0.5) - xy = transform(v_texCoord, v_stretch, v_sinangle, v_cosangle); - else - xy = (v_texCoord-vec2_splat(0.5))/overscan.xy+vec2_splat(0.5); - float cval = corner(xy); - - // Of all the pixels that are mapped onto the texel we are - // currently rendering, which pixel are we currently rendering? - vec2 ratio_scale = xy * u_tex_size0.xy - vec2_splat(0.5); + // Here's a helpful diagram to keep in mind while trying to + // understand the code: + // + // | | | | | + // ------------------------------- + // | | | | | + // | 01 | 11 | 21 | 31 | <-- current scanline + // | | @ | | | + // ------------------------------- + // | | | | | + // | 02 | 12 | 22 | 32 | <-- next scanline + // | | | | | + // ------------------------------- + // | | | | | + // + // Each character-cell represents a pixel on the output + // surface, "@" represents the current pixel (always somewhere + // in the bottom half of the current scan-line, or the top-half + // of the next scanline). The grid of lines represents the + // edges of the texels of the underlying texture. + + // Texture coordinates of the texel containing the active pixel. + vec2 xy; + if (curvature.x > 0.5) + xy = transform(v_texCoord, v_stretch, v_sinangle, v_cosangle); + else + xy = (v_texCoord-vec2_splat(0.5))/overscan.xy+vec2_splat(0.5); + float cval = corner(xy); + + // Of all the pixels that are mapped onto the texel we are + // currently rendering, which pixel are we currently rendering? + vec2 ratio_scale = xy * u_tex_size0.xy - vec2_splat(0.5); #ifdef OVERSAMPLE - float filter = fwidth(ratio_scale.y); + float filter = fwidth(ratio_scale.y); #endif - vec2 uv_ratio = fract(ratio_scale); + vec2 uv_ratio = fract(ratio_scale); - // Snap to the center of the underlying texel. - xy = (floor(ratio_scale) + vec2_splat(0.5)) / u_tex_size0.xy; + // Snap to the center of the underlying texel. + xy = (floor(ratio_scale) + vec2_splat(0.5)) / u_tex_size0.xy; - // Calculate scaling coefficients describing the effect - // of various neighbour texels in a scanline on the current - // pixel. - vec4 coeffs = x_coeffs(vec4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x), ratio_scale.x); + // Calculate scaling coefficients describing the effect + // of various neighbour texels in a scanline on the current + // pixel. + vec4 coeffs = x_coeffs(vec4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x), ratio_scale.x); - vec4 col = sample_scanline(xy, coeffs, v_one.x); - vec4 col2 = sample_scanline(xy + vec2(0.0, v_one.y), coeffs, v_one.x); + vec4 col = sample_scanline(xy, coeffs, v_one.x); + vec4 col2 = sample_scanline(xy + vec2(0.0, v_one.y), coeffs, v_one.x); #ifndef LINEAR_PROCESSING - col = pow(col , vec4_splat(CRTgamma.x)); - col2 = pow(col2, vec4_splat(CRTgamma.x)); + col = pow(col , vec4_splat(CRTgamma.x)); + col2 = pow(col2, vec4_splat(CRTgamma.x)); #endif - // Calculate the influence of the current and next scanlines on - // the current pixel. - vec4 weights = scanlineWeights(uv_ratio.y, col); - vec4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2); + // Calculate the influence of the current and next scanlines on + // the current pixel. + vec4 weights = scanlineWeights(uv_ratio.y, col); + vec4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2); #ifdef OVERSAMPLE - uv_ratio.y =uv_ratio.y+1.0/3.0*filter; - weights = (weights+scanlineWeights(uv_ratio.y, col))/3.0; - weights2=(weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2))/3.0; - uv_ratio.y =uv_ratio.y-2.0/3.0*filter; - weights=weights+scanlineWeights(abs(uv_ratio.y), col)/3.0; - weights2=weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2)/3.0; + uv_ratio.y =uv_ratio.y+1.0/3.0*filter; + weights = (weights+scanlineWeights(uv_ratio.y, col))/3.0; + weights2=(weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2))/3.0; + uv_ratio.y =uv_ratio.y-2.0/3.0*filter; + weights=weights+scanlineWeights(abs(uv_ratio.y), col)/3.0; + weights2=weights2+scanlineWeights(abs(1.0-uv_ratio.y), col2)/3.0; #endif - vec3 mul_res = (col * weights + col2 * weights2).rgb * vec3_splat(cval); + vec4 mul_res = col * weights + col2 * weights2 * vec4_splat(cval); - // Shadow mask - vec3 cout = apply_shadow_mask(v_texCoord.xy, mul_res); + // Shadow mask + vec3 cout = apply_shadow_mask(v_texCoord.xy, mul_res.rgb); - // Convert the image gamma for display on our output device. - cout = linear_to_output(cout); + // Convert the image gamma for display on our output device. + cout = linear_to_output(cout); - gl_FragColor = vec4(cout,1.0); + gl_FragColor = vec4(cout,mul_res.a); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussx.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussx.sc index 74faf15a740..6290ae330eb 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussx.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussx.sc @@ -6,33 +6,33 @@ uniform vec4 u_gamma; uniform vec4 u_tex_size0; SAMPLER2D(s_tex, 0); -#define TEX2D(v) pow(texture2D(s_tex, v).rgb, vec3_splat(u_gamma.x)) +#define TEX2D(v) pow(texture2D(s_tex, v), vec4(vec3_splat(u_gamma.x), 1.0)) void main() { - vec3 sum = vec3_splat(0.0); + vec4 sum = vec4_splat(0.0); float onex = 1.0/u_tex_size0.x; - sum += TEX2D(v_texCoord + vec2(-8.0 * onex, 0.0)) * vec3_splat(v_coeffs2.w); - sum += TEX2D(v_texCoord + vec2(-7.0 * onex, 0.0)) * vec3_splat(v_coeffs2.z); - sum += TEX2D(v_texCoord + vec2(-6.0 * onex, 0.0)) * vec3_splat(v_coeffs2.y); - sum += TEX2D(v_texCoord + vec2(-5.0 * onex, 0.0)) * vec3_splat(v_coeffs2.x); - sum += TEX2D(v_texCoord + vec2(-4.0 * onex, 0.0)) * vec3_splat(v_coeffs.w); - sum += TEX2D(v_texCoord + vec2(-3.0 * onex, 0.0)) * vec3_splat(v_coeffs.z); - sum += TEX2D(v_texCoord + vec2(-2.0 * onex, 0.0)) * vec3_splat(v_coeffs.y); - sum += TEX2D(v_texCoord + vec2(-1.0 * onex, 0.0)) * vec3_splat(v_coeffs.x); + sum += TEX2D(v_texCoord + vec2(-8.0 * onex, 0.0)) * vec4_splat(v_coeffs2.w); + sum += TEX2D(v_texCoord + vec2(-7.0 * onex, 0.0)) * vec4_splat(v_coeffs2.z); + sum += TEX2D(v_texCoord + vec2(-6.0 * onex, 0.0)) * vec4_splat(v_coeffs2.y); + sum += TEX2D(v_texCoord + vec2(-5.0 * onex, 0.0)) * vec4_splat(v_coeffs2.x); + sum += TEX2D(v_texCoord + vec2(-4.0 * onex, 0.0)) * vec4_splat(v_coeffs.w); + sum += TEX2D(v_texCoord + vec2(-3.0 * onex, 0.0)) * vec4_splat(v_coeffs.z); + sum += TEX2D(v_texCoord + vec2(-2.0 * onex, 0.0)) * vec4_splat(v_coeffs.y); + sum += TEX2D(v_texCoord + vec2(-1.0 * onex, 0.0)) * vec4_splat(v_coeffs.x); sum += TEX2D(v_texCoord); - sum += TEX2D(v_texCoord + vec2(+1.0 * onex, 0.0)) * vec3_splat(v_coeffs.x); - sum += TEX2D(v_texCoord + vec2(+2.0 * onex, 0.0)) * vec3_splat(v_coeffs.y); - sum += TEX2D(v_texCoord + vec2(+3.0 * onex, 0.0)) * vec3_splat(v_coeffs.z); - sum += TEX2D(v_texCoord + vec2(+4.0 * onex, 0.0)) * vec3_splat(v_coeffs.w); - sum += TEX2D(v_texCoord + vec2(+5.0 * onex, 0.0)) * vec3_splat(v_coeffs2.x); - sum += TEX2D(v_texCoord + vec2(+6.0 * onex, 0.0)) * vec3_splat(v_coeffs2.y); - sum += TEX2D(v_texCoord + vec2(+7.0 * onex, 0.0)) * vec3_splat(v_coeffs2.z); - sum += TEX2D(v_texCoord + vec2(+8.0 * onex, 0.0)) * vec3_splat(v_coeffs2.w); + sum += TEX2D(v_texCoord + vec2(+1.0 * onex, 0.0)) * vec4_splat(v_coeffs.x); + sum += TEX2D(v_texCoord + vec2(+2.0 * onex, 0.0)) * vec4_splat(v_coeffs.y); + sum += TEX2D(v_texCoord + vec2(+3.0 * onex, 0.0)) * vec4_splat(v_coeffs.z); + sum += TEX2D(v_texCoord + vec2(+4.0 * onex, 0.0)) * vec4_splat(v_coeffs.w); + sum += TEX2D(v_texCoord + vec2(+5.0 * onex, 0.0)) * vec4_splat(v_coeffs2.x); + sum += TEX2D(v_texCoord + vec2(+6.0 * onex, 0.0)) * vec4_splat(v_coeffs2.y); + sum += TEX2D(v_texCoord + vec2(+7.0 * onex, 0.0)) * vec4_splat(v_coeffs2.z); + sum += TEX2D(v_texCoord + vec2(+8.0 * onex, 0.0)) * vec4_splat(v_coeffs2.w); float norm = 1.0 / (1.0 + 2.0*(v_coeffs.x+v_coeffs.y+v_coeffs.z+v_coeffs.w +v_coeffs2.x+v_coeffs2.y+v_coeffs2.z+v_coeffs2.w)); - gl_FragColor = vec4( pow(sum*vec3_splat(norm), vec3_splat(1.0/u_gamma.x)), 1.0 ); + gl_FragColor = pow(sum*vec4_splat(norm), vec4(vec3_splat(1.0/u_gamma.x), 1.0)); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussy.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussy.sc index c4fa73cdcce..9f4eace6cbe 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussy.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_gaussy.sc @@ -6,33 +6,33 @@ uniform vec4 u_gamma; uniform vec4 u_tex_size0; SAMPLER2D(s_tex, 0); -#define TEX2D(v) pow(texture2D(s_tex, v).rgb, vec3_splat(u_gamma.x)) +#define TEX2D(v) pow(texture2D(s_tex, v), vec4(vec3_splat(u_gamma.x), 1.0)) void main() { - vec3 sum = vec3_splat(0.0); + vec4 sum = vec4_splat(0.0); float oney = 1.0/u_tex_size0.y; - sum += TEX2D(v_texCoord + vec2(0.0, -8.0 * oney)) * vec3_splat(v_coeffs2.w); - sum += TEX2D(v_texCoord + vec2(0.0, -7.0 * oney)) * vec3_splat(v_coeffs2.z); - sum += TEX2D(v_texCoord + vec2(0.0, -6.0 * oney)) * vec3_splat(v_coeffs2.y); - sum += TEX2D(v_texCoord + vec2(0.0, -5.0 * oney)) * vec3_splat(v_coeffs2.x); - sum += TEX2D(v_texCoord + vec2(0.0, -4.0 * oney)) * vec3_splat(v_coeffs.w); - sum += TEX2D(v_texCoord + vec2(0.0, -3.0 * oney)) * vec3_splat(v_coeffs.z); - sum += TEX2D(v_texCoord + vec2(0.0, -2.0 * oney)) * vec3_splat(v_coeffs.y); - sum += TEX2D(v_texCoord + vec2(0.0, -1.0 * oney)) * vec3_splat(v_coeffs.x); + sum += TEX2D(v_texCoord + vec2(0.0, -8.0 * oney)) * vec4_splat(v_coeffs2.w); + sum += TEX2D(v_texCoord + vec2(0.0, -7.0 * oney)) * vec4_splat(v_coeffs2.z); + sum += TEX2D(v_texCoord + vec2(0.0, -6.0 * oney)) * vec4_splat(v_coeffs2.y); + sum += TEX2D(v_texCoord + vec2(0.0, -5.0 * oney)) * vec4_splat(v_coeffs2.x); + sum += TEX2D(v_texCoord + vec2(0.0, -4.0 * oney)) * vec4_splat(v_coeffs.w); + sum += TEX2D(v_texCoord + vec2(0.0, -3.0 * oney)) * vec4_splat(v_coeffs.z); + sum += TEX2D(v_texCoord + vec2(0.0, -2.0 * oney)) * vec4_splat(v_coeffs.y); + sum += TEX2D(v_texCoord + vec2(0.0, -1.0 * oney)) * vec4_splat(v_coeffs.x); sum += TEX2D(v_texCoord); - sum += TEX2D(v_texCoord + vec2(0.0, +1.0 * oney)) * vec3_splat(v_coeffs.x); - sum += TEX2D(v_texCoord + vec2(0.0, +2.0 * oney)) * vec3_splat(v_coeffs.y); - sum += TEX2D(v_texCoord + vec2(0.0, +3.0 * oney)) * vec3_splat(v_coeffs.z); - sum += TEX2D(v_texCoord + vec2(0.0, +4.0 * oney)) * vec3_splat(v_coeffs.w); - sum += TEX2D(v_texCoord + vec2(0.0, +5.0 * oney)) * vec3_splat(v_coeffs2.x); - sum += TEX2D(v_texCoord + vec2(0.0, +6.0 * oney)) * vec3_splat(v_coeffs2.y); - sum += TEX2D(v_texCoord + vec2(0.0, +7.0 * oney)) * vec3_splat(v_coeffs2.z); - sum += TEX2D(v_texCoord + vec2(0.0, +8.0 * oney)) * vec3_splat(v_coeffs2.w); + sum += TEX2D(v_texCoord + vec2(0.0, +1.0 * oney)) * vec4_splat(v_coeffs.x); + sum += TEX2D(v_texCoord + vec2(0.0, +2.0 * oney)) * vec4_splat(v_coeffs.y); + sum += TEX2D(v_texCoord + vec2(0.0, +3.0 * oney)) * vec4_splat(v_coeffs.z); + sum += TEX2D(v_texCoord + vec2(0.0, +4.0 * oney)) * vec4_splat(v_coeffs.w); + sum += TEX2D(v_texCoord + vec2(0.0, +5.0 * oney)) * vec4_splat(v_coeffs2.x); + sum += TEX2D(v_texCoord + vec2(0.0, +6.0 * oney)) * vec4_splat(v_coeffs2.y); + sum += TEX2D(v_texCoord + vec2(0.0, +7.0 * oney)) * vec4_splat(v_coeffs2.z); + sum += TEX2D(v_texCoord + vec2(0.0, +8.0 * oney)) * vec4_splat(v_coeffs2.w); float norm = 1.0 / (1.0 + 2.0*(v_coeffs.x+v_coeffs.y+v_coeffs.z+v_coeffs.w +v_coeffs2.x+v_coeffs2.y+v_coeffs2.z+v_coeffs2.w)); - gl_FragColor = vec4( pow(sum*vec3_splat(norm), vec3_splat(1.0/u_gamma.x)), 1.0 ); + gl_FragColor = pow(sum*vec4_splat(norm), vec4(vec3_splat(1.0/u_gamma.x), 1.0)); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_lowpass.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_lowpass.sc index 2f48bbac321..37ab685957c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_lowpass.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_lowpass.sc @@ -33,5 +33,5 @@ void main() sum +=sample_screen(v_texCoord+vec2(-7.0*onex,0.0))*vec3_splat(v_lpcoeffs2.w); sum +=sample_screen(v_texCoord+vec2( 7.0*onex,0.0))*vec3_splat(v_lpcoeffs2.w); - gl_FragColor = vec4( sum, 1.0 ); + gl_FragColor = vec4( sum, texture2D(s_screen, v_texCoord).a ); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_mipmap8.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_mipmap8.sc index 0f86bdbc7ba..2e0f8038053 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_mipmap8.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_mipmap8.sc @@ -22,7 +22,7 @@ uniform vec4 u_smooth; const vec2 v05 = vec2(0.5,0.5); // pix has integer coordinates -> average four pixels using bilinear filtering -vec3 sample_screen(vec2 pix) +vec4 sample_screen(vec2 pix) { vec2 border = step(-0.5, u_tex_size0.xy - pix); float weight = border.x * border.y; @@ -34,12 +34,12 @@ vec3 sample_screen(vec2 pix) border = clamp(u_tex_size0.xy-x,0.0,1.0); weight *= border.x * border.y; - vec3 tex = texture2D(s_screen, x / u_tex_size0.xy).rgb; + vec4 tex = texture2D(s_screen, x / u_tex_size0.xy); - return tex * vec3_splat(weight); + return tex * vec4_splat(weight); } -vec3 sample_mipmap(vec2 pix, vec2 offset, vec2 size) +vec4 sample_mipmap(vec2 pix, vec2 offset, vec2 size) { vec2 border = step(-0.5, size - pix) * step(-0.5, pix); float weight = border.x * border.y; @@ -51,9 +51,9 @@ vec3 sample_mipmap(vec2 pix, vec2 offset, vec2 size) border = clamp(size-x,0.0,1.0); weight *= border.x * border.y; - vec3 tex = texture2D(s_mipmap, (x + offset) / u_tex_size0.xy).rgb; + vec4 tex = texture2D(s_mipmap, (x + offset) / u_tex_size0.xy); - return tex * vec3_splat(weight); + return tex * vec4_splat(weight); } void main() @@ -66,7 +66,7 @@ void main() // sample 4x4 grid // each sample uses bilinear filtering to average a 2x2 region // overall reduction by 8x8 - vec3 tex = ( sample_screen(x + vec2(-3.0,-3.0)) + vec4 tex = ( sample_screen(x + vec2(-3.0,-3.0)) + sample_screen(x + vec2(-3.0,-1.0)) + sample_screen(x + vec2(-3.0, 1.0)) + sample_screen(x + vec2(-3.0, 3.0)) @@ -113,7 +113,7 @@ void main() if (fc) x = vec2(4.0,4.0); - vec3 tex = ( sample_mipmap(x + vec2(-3.0,-3.0),offset0,newsize) + vec4 tex = ( sample_mipmap(x + vec2(-3.0,-3.0),offset0,newsize) + sample_mipmap(x + vec2(-3.0,-1.0),offset0,newsize) + sample_mipmap(x + vec2(-3.0, 1.0),offset0,newsize) + sample_mipmap(x + vec2(-3.0, 3.0),offset0,newsize) @@ -133,16 +133,16 @@ void main() if (fc) { // this sample yields average over the whole screen // apply a compensation for the zeros included in the 8^n average float f = fac*fac / (u_tex_size0.x * u_tex_size0.y); - vec3 col = tex * vec3_splat(f); + vec4 col = tex * vec4_splat(f); if (v_texCoord.y * u_tex_size1.y < ( offset0.y + u_tex_size1.y ) * 0.5) { - gl_FragColor = vec4(col,1.0); + gl_FragColor = col; } else { // we are near (1,1): in this region include the temporal filter vec4 old = texture2D(s_mipmap, v_texCoord.xy); - gl_FragColor = vec4(mix(col,old.rgb,u_smooth.x),1.0); + gl_FragColor = mix(col,old,u_smooth.x); } } else { - gl_FragColor = vec4(tex.rgb,1.0); + gl_FragColor = tex; } } } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_phosphor_apply.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_phosphor_apply.sc index c45fb8f883f..f7e116696cb 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_phosphor_apply.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/fs_phosphor_apply.sc @@ -28,15 +28,15 @@ void main() vec4 screen = texture2D(s_screen, v_texCoord); vec4 phosphor = texture2D(s_phosphor, v_texCoord); - vec3 cscrn = pow(screen.rgb, vec3_splat(u_gamma.x)); - vec3 cphos = pow(phosphor.rgb, vec3_splat(u_gamma.x)); + vec4 cscrn = pow(screen, vec4(vec3_splat(u_gamma.x), 1.0)); + vec4 cphos = pow(phosphor, vec4(vec3_splat(u_gamma.x), 1.0)); // encode the upper 2 bits of the time elapsed in the lower 2 bits of b float t = 255.0*phosphor.a + fract(phosphor.b*255.0/4.0)*1024.0; - cphos *= vec3_splat( u_phosphor_amplitude.x * decay(t) ); + cphos *= vec4_splat( u_phosphor_amplitude.x * decay(t) ); - vec3 col = pow(cscrn + cphos, vec3_splat(1.0/u_gamma.x)); + vec4 col = pow(cscrn + cphos, vec4(vec3_splat(1.0/u_gamma.x), 1.0)); - gl_FragColor = vec4(col, 1.0); + gl_FragColor = col; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_crt-geom.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_crt-geom.sc index cc3ed09aa14..4f17f0d0ec3 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_crt-geom.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_crt-geom.sc @@ -19,78 +19,82 @@ uniform vec4 R; uniform vec4 angle; uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; uniform vec4 u_rotation_type; #define FIX(c) max(abs(c), 1e-5) float intersect(vec2 xy , vec2 sinangle, vec2 cosangle) { - float A = dot(xy,xy)+d.x*d.x; - float B = 2.0*(R.x*(dot(xy,sinangle)-d.x*cosangle.x*cosangle.y)-d.x*d.x); - float C = d.x*d.x + 2.0*R.x*d.x*cosangle.x*cosangle.y; - return (-B-sqrt(B*B-4.0*A*C))/(2.0*A); + float A = dot(xy,xy)+d.x*d.x; + float B = 2.0*(R.x*(dot(xy,sinangle)-d.x*cosangle.x*cosangle.y)-d.x*d.x); + float C = d.x*d.x + 2.0*R.x*d.x*cosangle.x*cosangle.y; + return (-B-sqrt(B*B-4.0*A*C))/(2.0*A); } vec2 bkwtrans(vec2 xy, vec2 sinangle, vec2 cosangle) { - float c = intersect(xy, sinangle, cosangle); - vec2 pt = vec2_splat(c)*xy; - pt -= vec2_splat(-R.x)*sinangle; - pt /= vec2_splat(R.x); - vec2 tang = sinangle/cosangle; - vec2 poc = pt/cosangle; - float A = dot(tang,tang)+1.0; - float B = -2.0*dot(poc,tang); - float C = dot(poc,poc)-1.0; - float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A); - vec2 uv = (pt-a*sinangle)/cosangle; - float r = FIX(R.x*acos(a)); - return uv*r/sin(r/R.x); + float c = intersect(xy, sinangle, cosangle); + vec2 pt = vec2_splat(c)*xy; + pt -= vec2_splat(-R.x)*sinangle; + pt /= vec2_splat(R.x); + vec2 tang = sinangle/cosangle; + vec2 poc = pt/cosangle; + float A = dot(tang,tang)+1.0; + float B = -2.0*dot(poc,tang); + float C = dot(poc,poc)-1.0; + float a = (-B+sqrt(B*B-4.0*A*C))/(2.0*A); + vec2 uv = (pt-a*sinangle)/cosangle; + float r = FIX(R.x*acos(a)); + return uv*r/sin(r/R.x); } vec2 fwtrans(vec2 uv, vec2 sinangle, vec2 cosangle) { - float r = FIX(sqrt(dot(uv,uv))); - uv *= sin(r/R.x)/r; - float x = 1.0-cos(r/R.x); - float D = d.x/R.x + x*cosangle.x*cosangle.y+dot(uv,sinangle); - return d.x*(uv*cosangle-x*sinangle)/D; + float r = FIX(sqrt(dot(uv,uv))); + uv *= sin(r/R.x)/r; + float x = 1.0-cos(r/R.x); + float D = d.x/R.x + x*cosangle.x*cosangle.y+dot(uv,sinangle); + return d.x*(uv*cosangle-x*sinangle)/D; } vec3 maxscale(vec2 sinangle, vec2 cosangle) { - vec2 c = bkwtrans(-R.x * sinangle / (1.0 + R.x/d.x*cosangle.x*cosangle.y), sinangle, cosangle); - vec2 a = vec2(0.5,0.5)*aspect.xy; - vec2 lo = vec2(fwtrans(vec2(-a.x,c.y), sinangle, cosangle).x, - fwtrans(vec2(c.x,-a.y), sinangle, cosangle).y)/aspect.xy; - vec2 hi = vec2(fwtrans(vec2(+a.x,c.y), sinangle, cosangle).x, - fwtrans(vec2(c.x,+a.y), sinangle, cosangle).y)/aspect.xy; - return vec3((hi+lo)*aspect.xy*0.5,max(hi.x-lo.x,hi.y-lo.y)); + vec2 c = bkwtrans(-R.x * sinangle / (1.0 + R.x/d.x*cosangle.x*cosangle.y), sinangle, cosangle); + vec2 a = vec2(0.5,0.5)*aspect.xy; + vec2 lo = vec2(fwtrans(vec2(-a.x,c.y), sinangle, cosangle).x, + fwtrans(vec2(c.x,-a.y), sinangle, cosangle).y)/aspect.xy; + vec2 hi = vec2(fwtrans(vec2(+a.x,c.y), sinangle, cosangle).x, + fwtrans(vec2(c.x,+a.y), sinangle, cosangle).y)/aspect.xy; + return vec3((hi+lo)*aspect.xy*0.5,max(hi.x-lo.x,hi.y-lo.y)); } void main() { - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; - // Precalculate a bunch of useful values we'll need in the fragment - // shader. - vec2 ang; - // if (u_rotation_type.x < 0.5) - // ang = vec2(0.0,angle.x); - // else if (u_rotation_type.x < 1.5) - // ang = vec2(angle.x,0.0); - // else if (u_rotation_type.x < 2.5) - // ang = vec2(0.0,-angle.x); - // else - // ang = vec2(-angle.x,0.0); - ang = angle.xy; - v_sinangle = sin(ang); - v_cosangle = cos(ang); - v_stretch = maxscale(v_sinangle, v_cosangle); + // Precalculate a bunch of useful values we'll need in the fragment + // shader. + vec2 ang; + // if (u_rotation_type.x < 0.5) + // ang = vec2(0.0,angle.x); + // else if (u_rotation_type.x < 1.5) + // ang = vec2(angle.x,0.0); + // else if (u_rotation_type.x < 2.5) + // ang = vec2(0.0,-angle.x); + // else + // ang = vec2(-angle.x,0.0); + ang = angle.xy; + v_sinangle = sin(ang); + v_cosangle = cos(ang); + v_stretch = maxscale(v_sinangle, v_cosangle); - // The size of one texel, in texture-coordinates. - v_one = 1.0 / u_tex_size0.xy; + // The size of one texel, in texture-coordinates. + v_one = 1.0 / u_tex_size0.xy; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussx.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussx.sc index e9969187c94..a5fb1d22622 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussx.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussx.sc @@ -4,16 +4,20 @@ $output v_texCoord, v_coeffs, v_coeffs2 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; uniform vec4 u_width; uniform vec4 u_aspect; void main() { - float wid = u_width.x*u_tex_size0.x/(320.*u_aspect.x); - v_coeffs = exp(vec4(1.,4.,9.,16.)*vec4_splat(-1.0/wid/wid)); - v_coeffs2 = exp(vec4(25.,36.,49.,64.)*vec4_splat(-1.0/wid/wid)); + float wid = u_width.x*u_tex_size0.x/(320.*u_aspect.x); + v_coeffs = exp(vec4(1.,4.,9.,16.)*vec4_splat(-1.0/wid/wid)); + v_coeffs2 = exp(vec4(25.,36.,49.,64.)*vec4_splat(-1.0/wid/wid)); - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussy.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussy.sc index 0b7ff9b411f..93676cde1d5 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussy.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_gaussy.sc @@ -4,16 +4,20 @@ $output v_texCoord, v_coeffs, v_coeffs2 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; uniform vec4 u_width; uniform vec4 u_aspect; void main() { - float wid = u_width.x*u_tex_size0.y/(320.*u_aspect.y); - v_coeffs = exp(vec4(1.,4.,9.,16.)*vec4_splat(-1.0/wid/wid)); - v_coeffs2 = exp(vec4(25.,36.,49.,64.)*vec4_splat(-1.0/wid/wid)); + float wid = u_width.x*u_tex_size0.y/(320.*u_aspect.y); + v_coeffs = exp(vec4(1.,4.,9.,16.)*vec4_splat(-1.0/wid/wid)); + v_coeffs2 = exp(vec4(25.,36.,49.,64.)*vec4_splat(-1.0/wid/wid)); - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_lowpass.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_lowpass.sc index 4d948a63a89..2ca983d41d7 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_lowpass.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_lowpass.sc @@ -4,46 +4,50 @@ $output v_texCoord, v_lpcoeffs1, v_lpcoeffs2 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; uniform vec4 u_lowpass_cutoff; uniform vec4 u_lowpass_width; void main() { - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; - // interpret the cutoff and width as target resolutions in pixels per scanline - // include factor of 1/2 because max frequency at given resolution is Nyquist - float a = 0.5 * u_lowpass_cutoff.x / u_tex_size0.x; - float b = 0.5 * u_lowpass_width.x / u_tex_size0.x; - float w0 = (a-0.5*b); - float w1 = (a+0.5*b); + // interpret the cutoff and width as target resolutions in pixels per scanline + // include factor of 1/2 because max frequency at given resolution is Nyquist + float a = 0.5 * u_lowpass_cutoff.x / u_tex_size0.x; + float b = 0.5 * u_lowpass_width.x / u_tex_size0.x; + float w0 = (a-0.5*b); + float w1 = (a+0.5*b); - float two_pi = 6.283185307179586; - vec3 n1 = vec3(1.0,2.0,3.0); - vec4 n2 = vec4(4.0,5.0,6.0,7.0); + float two_pi = 6.283185307179586; + vec3 n1 = vec3(1.0,2.0,3.0); + vec4 n2 = vec4(4.0,5.0,6.0,7.0); - // target frequency response: - // 1 for w < w0 - // 0 for w > w1 - // linearly decreasing for w0 < w < w1 - // this will be approximated by including the lowest Fourier modes - if (w0 > 0.5) { // no filtering - v_lpcoeffs1 = vec4(1.0,0.0,0.0,0.0); - v_lpcoeffs2 = vec4_splat(0.0); - } else if (w1 > 0.5) { // don't reach zero - // here the target has a nonzero response at the Nyquist frequency - v_lpcoeffs1.x = w1 + w0 - (w1 - 0.5)*(w1 - 0.5)/(w1 - w0); - v_lpcoeffs1.yzw = 2.0 / ( two_pi*two_pi*(w1-w0)*n1*n1 ) * ( cos(two_pi*w0*n1) - cos(two_pi*0.5*n1) ); - v_lpcoeffs2 = 2.0 / ( two_pi*two_pi*(w1-w0)*n2*n2 ) * ( cos(two_pi*w0*n2) - cos(two_pi*0.5*n2) ); - } else if (w1 == w0) { // sharp cutoff - v_lpcoeffs1.x = 2.0 * w0; - v_lpcoeffs1.yzw = 2.0 / ( two_pi * n1 ) * sin(two_pi*w0*n1); - v_lpcoeffs2 = 2.0 / ( two_pi * n2 ) * sin(two_pi*w0*n2); - } else { - v_lpcoeffs1.x = w1 + w0; - v_lpcoeffs1.yzw = 2.0 / ( two_pi*two_pi*(w1-w0)*n1*n1 ) * ( cos(two_pi*w0*n1) - cos(two_pi*w1*n1) ); - v_lpcoeffs2 = 2.0 / ( two_pi*two_pi*(w1-w0)*n2*n2 ) * ( cos(two_pi*w0*n2) - cos(two_pi*w1*n2) ); - } + // target frequency response: + // 1 for w < w0 + // 0 for w > w1 + // linearly decreasing for w0 < w < w1 + // this will be approximated by including the lowest Fourier modes + if (w0 > 0.5) { // no filtering + v_lpcoeffs1 = vec4(1.0,0.0,0.0,0.0); + v_lpcoeffs2 = vec4_splat(0.0); + } else if (w1 > 0.5) { // don't reach zero + // here the target has a nonzero response at the Nyquist frequency + v_lpcoeffs1.x = w1 + w0 - (w1 - 0.5)*(w1 - 0.5)/(w1 - w0); + v_lpcoeffs1.yzw = 2.0 / ( two_pi*two_pi*(w1-w0)*n1*n1 ) * ( cos(two_pi*w0*n1) - cos(two_pi*0.5*n1) ); + v_lpcoeffs2 = 2.0 / ( two_pi*two_pi*(w1-w0)*n2*n2 ) * ( cos(two_pi*w0*n2) - cos(two_pi*0.5*n2) ); + } else if (w1 == w0) { // sharp cutoff + v_lpcoeffs1.x = 2.0 * w0; + v_lpcoeffs1.yzw = 2.0 / ( two_pi * n1 ) * sin(two_pi*w0*n1); + v_lpcoeffs2 = 2.0 / ( two_pi * n2 ) * sin(two_pi*w0*n2); + } else { + v_lpcoeffs1.x = w1 + w0; + v_lpcoeffs1.yzw = 2.0 / ( two_pi*two_pi*(w1-w0)*n1*n1 ) * ( cos(two_pi*w0*n1) - cos(two_pi*w1*n1) ); + v_lpcoeffs2 = 2.0 / ( two_pi*two_pi*(w1-w0)*n2*n2 ) * ( cos(two_pi*w0*n2) - cos(two_pi*w1*n2) ); + } } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_mipmap8.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_mipmap8.sc index bdd09148403..8424e153cdb 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_mipmap8.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_mipmap8.sc @@ -3,9 +3,14 @@ $output v_texCoord #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_apply.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_apply.sc index bdd09148403..8424e153cdb 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_apply.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_apply.sc @@ -3,9 +3,14 @@ $output v_texCoord #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_update.sc b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_update.sc index bdd09148403..8424e153cdb 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_update.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt-geom/vs_phosphor_update.sc @@ -3,9 +3,14 @@ $output v_texCoord #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - // Do the standard vertex processing. - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); - v_texCoord = a_texcoord0; + // Do the standard vertex processing. + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + v_texCoord = a_texcoord0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/crt/vs_crt-caligari.sc b/src/osd/modules/render/bgfx/shaders/chains/crt/vs_crt-caligari.sc index 6dead210d9e..ce22c1e7200 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/crt/vs_crt-caligari.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/crt/vs_crt-caligari.sc @@ -32,10 +32,14 @@ $output v_texcoord0, v_texcoord1, v_color0 // Autos uniform vec4 u_source_size; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; onex = vec2(1.0 / u_source_size.x, 0.0); diff --git a/src/osd/modules/render/bgfx/shaders/chains/default/vs_blit.sc b/src/osd/modules/render/bgfx/shaders/chains/default/vs_blit.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/default/vs_blit.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/default/vs_blit.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/eagle/fs_eagle.sc b/src/osd/modules/render/bgfx/shaders/chains/eagle/fs_eagle.sc index 5219614c794..ca769f5dc07 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/eagle/fs_eagle.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/eagle/fs_eagle.sc @@ -122,16 +122,16 @@ void main() else { p01 = 0.5 * (C4 + C5); - } + } - if ((c8 == d2) || (c3 == c4)) - { + if ((c8 == d2) || (c3 == c4)) + { p10 = 0.25 * (3.0 * C4 + C7); - } - else - { + } + else + { p10 = 0.5 * (C7 + C8); - } + } } else { diff --git a/src/osd/modules/render/bgfx/shaders/chains/eagle/vs_eagle.sc b/src/osd/modules/render/bgfx/shaders/chains/eagle/vs_eagle.sc index 1ab076dfa73..095d5d562e0 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/eagle/vs_eagle.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/eagle/vs_eagle.sc @@ -14,10 +14,14 @@ $output v_color0, texCoord, t1, t2, t3, t4, t5, t6, t7, t8 // Autos uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_color0 = a_color0; vec2 ps = vec2(1.0, 1.0) / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_defocus.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_defocus.sc index cfb7b487596..20820f0c0a3 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_defocus.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_defocus.sc @@ -25,7 +25,7 @@ void main() { // previously this pass was applied two times with offsets of 0.25, 0.5, 0.75, 1.0 // now this pass is applied only once with offsets of 0.25, 0.55, 1.0, 1.6 to achieve the same appearance as before till a maximum defocus of 2.0 - // 0.075x² + 0.225x + 0.25 + // 0.075x² + 0.225x + 0.25 const vec2 Coord1Offset = vec2(-1.60, 0.25); const vec2 Coord2Offset = vec2(-1.00, -0.55); const vec2 Coord3Offset = vec2(-0.55, 1.00); diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_distortion.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_distortion.sc index 47201c0661d..ed4f6195944 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_distortion.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_distortion.sc @@ -134,11 +134,11 @@ vec2 GetDistortedCoords(vec2 centerCoord, float amount, float amountCube) float r2 = centerCoord.x * centerCoord.x + centerCoord.y * centerCoord.y; float f = kcube == 0.0 ? 1.0 + r2 * k : 1.0 + r2 * (k + kcube * sqrt(r2)); - // fit screen bounds + // fit screen bounds f /= 1.0 + amount * 0.25 + amountCube * 0.125; // apply cubic distortion factor - centerCoord *= f; + centerCoord *= f; return centerCoord; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_scanline.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_scanline.sc index 5942c75bab6..2126398062b 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_scanline.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/fs_scanline.sc @@ -37,34 +37,15 @@ SAMPLER2D(s_shadow, 1); // Scanline Pixel Shader //----------------------------------------------------------------------------- -vec2 GetAdjustedCoords(vec2 coord) -{ - // center coordinates - coord -= 0.5; - - // apply screen scale - coord *= u_screen_scale.xy; - - // un-center coordinates - coord += 0.5; - - // apply screen offset - coord += u_screen_offset.xy; - - return coord; -} - void main() { - vec2 BaseCoord = GetAdjustedCoords(v_texcoord0); - // Color - vec4 BaseColor = texture2D(s_tex, BaseCoord); + vec4 BaseColor = texture2D(s_tex, v_texcoord0); // Clamp - if (BaseCoord.x < 0.0 || BaseCoord.y < 0.0 || BaseCoord.x > 1.0 || BaseCoord.y > 1.0) + if (v_texcoord0.x < 0.0 || v_texcoord0.y < 0.0 || v_texcoord0.x > 1.0 || v_texcoord0.y > 1.0) { - gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); } else { @@ -76,7 +57,7 @@ void main() float ColorBrightness = 0.299 * BaseColor.r + 0.587 * BaseColor.g + 0.114 * BaseColor.b; - float ScanCoord = BaseCoord.y; + float ScanCoord = v_texcoord0.y; ScanCoord += u_swap_xy.x > 0.0 ? u_quad_dims.x <= u_source_dims.x * 2.0 ? 0.5 / u_quad_dims.x // uncenter scanlines if the quad is less than twice the size of the source diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_chroma.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_chroma.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_chroma.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_chroma.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_color.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_color.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_color.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_color.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_deconverge.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_deconverge.sc index b77d85d1ea8..55c2f0e2e34 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_deconverge.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_deconverge.sc @@ -8,6 +8,7 @@ $output v_color0, v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3 // Autos uniform vec4 u_source_size; +uniform vec4 u_inv_view_dims; // User-supplied uniform vec4 u_converge_red; @@ -20,6 +21,9 @@ uniform vec4 u_radial_converge_blue; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif vec2 half_value = vec2(0.5, 0.5); diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_defocus.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_defocus.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_defocus.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_defocus.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_distortion.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_distortion.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_distortion.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_distortion.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_decode.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_decode.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_decode.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_decode.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_encode.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_encode.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_encode.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_ntsc_encode.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_phosphor.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_phosphor.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_phosphor.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_phosphor.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_post.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_post.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_post.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_post.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_prescale.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_prescale.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_prescale.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_prescale.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_scanline.sc b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_scanline.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_scanline.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hlsl/vs_scanline.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq2x.sc b/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq2x.sc index 479ed825a8b..3f6c2645ba4 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq2x.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq2x.sc @@ -60,8 +60,8 @@ void main() vec2 index; index.x = dot(pattern[0], vec3( 1.0, 2.0, 4.0)) + - dot(pattern[1], vec3( 8.0, 0.0, 16.0)) + - dot(pattern[2], vec3(32.0, 64.0, 128.0)); + dot(pattern[1], vec3( 8.0, 0.0, 16.0)) + + dot(pattern[2], vec3(32.0, 64.0, 128.0)); index.y = dot(cross, vec4(1.0, 2.0, 4.0, 8.0)) * SCALE * SCALE + dot(floor(fp * vec2(SCALE, SCALE)), vec2(1.0, SCALE)); vec2 step = vec2(1.0, 1.0) / vec2(256.0, 16.0 * (SCALE * SCALE)); diff --git a/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq3x.sc b/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq3x.sc index 1d35cec01e6..1cb08a52da4 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq3x.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq3x.sc @@ -60,8 +60,8 @@ void main() vec2 index; index.x = dot(pattern[0], vec3( 1.0, 2.0, 4.0)) + - dot(pattern[1], vec3( 8.0, 0.0, 16.0)) + - dot(pattern[2], vec3(32.0, 64.0, 128.0)); + dot(pattern[1], vec3( 8.0, 0.0, 16.0)) + + dot(pattern[2], vec3(32.0, 64.0, 128.0)); index.y = dot(cross, vec4(1.0, 2.0, 4.0, 8.0)) * SCALE * SCALE + dot(floor(fp * vec2(SCALE, SCALE)), vec2(1.0, SCALE)); vec2 step = vec2(1.0, 1.0) / vec2(256.0, 16.0 * (SCALE * SCALE)); diff --git a/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq4x.sc b/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq4x.sc index 6f25b0e9cc3..6e87d47dc0c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq4x.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hqx/fs_hq4x.sc @@ -60,8 +60,8 @@ void main() vec2 index; index.x = dot(pattern[0], vec3( 1.0, 2.0, 4.0)) + - dot(pattern[1], vec3( 8.0, 0.0, 16.0)) + - dot(pattern[2], vec3(32.0, 64.0, 128.0)); + dot(pattern[1], vec3( 8.0, 0.0, 16.0)) + + dot(pattern[2], vec3(32.0, 64.0, 128.0)); index.y = dot(cross, vec4(1.0, 2.0, 4.0, 8.0)) * SCALE * SCALE + dot(floor(fp * vec2(SCALE, SCALE)), vec2(1.0, SCALE)); vec2 step = vec2(1.0, 1.0) / vec2(256.0, 16.0 * (SCALE * SCALE)); diff --git a/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq2x.sc b/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq2x.sc index dcff07b17f5..727f5763857 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq2x.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq2x.sc @@ -8,10 +8,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_color0 // Autos uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_color0 = a_color0; vec2 ps = vec2(1.0, 1.0) / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq3x.sc b/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq3x.sc index dcff07b17f5..727f5763857 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq3x.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq3x.sc @@ -8,10 +8,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_color0 // Autos uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_color0 = a_color0; vec2 ps = vec2(1.0, 1.0) / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq4x.sc b/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq4x.sc index dcff07b17f5..727f5763857 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq4x.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/hqx/vs_hq4x.sc @@ -8,10 +8,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_color0 // Autos uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_color0 = a_color0; vec2 ps = vec2(1.0, 1.0) / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_lcd-grid.sc b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_lcd-grid.sc index 9ed2a47e801..801d1a2e9a4 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_lcd-grid.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_lcd-grid.sc @@ -22,85 +22,85 @@ uniform vec4 u_subpixsize; vec3 fetch_offset(vec2 coord, vec2 offset) { - vec2 x = coord + offset; - vec3 col = texture2D(s_tex, x).rgb; - vec3 col2 = pow(vec3_splat(u_gain.x) * col + vec3_splat(u_blacklevel.x), vec3_splat(u_LCDgamma.x)) + vec3_splat(u_ambient.x); - // want zero for outside pixels - vec4 b = step(0.0,vec4(x.x,x.y,1.0-x.x,1.0-x.y)); - return vec3_splat(b.x*b.y*b.z*b.w) * col2; + vec2 x = coord + offset; + vec3 col = texture2D(s_tex, x).rgb; + vec3 col2 = pow(vec3_splat(u_gain.x) * col + vec3_splat(u_blacklevel.x), vec3_splat(u_LCDgamma.x)) + vec3_splat(u_ambient.x); + // want zero for outside pixels + vec4 b = step(0.0,vec4(x.x,x.y,1.0-x.x,1.0-x.y)); + return vec3_splat(b.x*b.y*b.z*b.w) * col2; } float intsmear_func(float z, float c1, float c2, float c3, float c4, float c5, float c6) { - float z2 = z*z; - // both cases have c0=1 - return z * (1.0 + z2*(c1 + z2*(c2 + z2*(c3 + z2*(c4 + z2*(c5 + z2*c6)))))); + float z2 = z*z; + // both cases have c0=1 + return z * (1.0 + z2*(c1 + z2*(c2 + z2*(c3 + z2*(c4 + z2*(c5 + z2*c6)))))); } float intsmear_fx(float z) { - // integral of (1 - x^2 - x^4 + x^6)^2 - return intsmear_func(z, -2.0/3.0, -1.0/5.0, 4.0/7.0, -1.0/9.0, -2.0/11.0, 1.0/13.0); + // integral of (1 - x^2 - x^4 + x^6)^2 + return intsmear_func(z, -2.0/3.0, -1.0/5.0, 4.0/7.0, -1.0/9.0, -2.0/11.0, 1.0/13.0); } float intsmear_fy(float z) { - // integral of (1 - 2x^4 + x^6)^2 - return intsmear_func(z, 0.0, -4.0/5.0, 2.0/7.0, 4.0/9.0, -4.0/11.0, 1.0/13.0); + // integral of (1 - 2x^4 + x^6)^2 + return intsmear_func(z, 0.0, -4.0/5.0, 2.0/7.0, 4.0/9.0, -4.0/11.0, 1.0/13.0); } float intsmear_x(float x, float dx, float d) { - float zl = clamp((x-dx*0.5)/d,-1.0,1.0); - float zh = clamp((x+dx*0.5)/d,-1.0,1.0); - return d * ( intsmear_fx(zh) - intsmear_fx(zl) )/dx; + float zl = clamp((x-dx*0.5)/d,-1.0,1.0); + float zh = clamp((x+dx*0.5)/d,-1.0,1.0); + return d * ( intsmear_fx(zh) - intsmear_fx(zl) )/dx; } float intsmear_y(float x, float dx, float d) { - float zl = clamp((x-dx*0.5)/d,-1.0,1.0); - float zh = clamp((x+dx*0.5)/d,-1.0,1.0); - return d * ( intsmear_fy(zh) - intsmear_fy(zl) )/dx; + float zl = clamp((x-dx*0.5)/d,-1.0,1.0); + float zh = clamp((x+dx*0.5)/d,-1.0,1.0); + return d * ( intsmear_fy(zh) - intsmear_fy(zl) )/dx; } void main() { - vec3 cr = pow(u_rsubpix.rgb, vec3_splat(u_monitorgamma.x)); - vec3 cg = pow(u_gsubpix.rgb, vec3_splat(u_monitorgamma.x)); - vec3 cb = pow(u_bsubpix.rgb, vec3_splat(u_monitorgamma.x)); - - vec2 tl = v_texcoord0 * u_tex_size0.xy - vec2(0.5,0.5); - vec2 tli = floor(tl); - float subpix = fract(tl.x) * 3.0; - float rsubpix = u_tex_size0.x / u_quad_dims.x * 3.0; - - vec3 lcol, rcol; - lcol = vec3(intsmear_x(subpix+1.0, rsubpix, 3.0*u_subpixsize.x), - intsmear_x(subpix , rsubpix, 3.0*u_subpixsize.x), - intsmear_x(subpix-1.0, rsubpix, 3.0*u_subpixsize.x)); - rcol = vec3(intsmear_x(subpix-2.0, rsubpix, 3.0*u_subpixsize.x), - intsmear_x(subpix-3.0, rsubpix, 3.0*u_subpixsize.x), - intsmear_x(subpix-4.0, rsubpix, 3.0*u_subpixsize.x)); - - if (u_BGR.x > 0.5) { - lcol.rgb = lcol.bgr; - rcol.rgb = rcol.bgr; - } - - float tcol, bcol; - subpix = fract(tl.y); - rsubpix = u_tex_size0.y / u_quad_dims.y; - tcol = intsmear_y(subpix ,rsubpix, u_subpixsize.y); - bcol = intsmear_y(subpix-1.0,rsubpix, u_subpixsize.y); - - tl = (tli + vec2_splat(0.5)) / u_tex_size0.xy; - vec2 one = vec2_splat(1.0) / u_tex_size0.xy; - - vec3 ul = fetch_offset(tl, vec2(0,0) ) * lcol * vec3_splat(tcol); - vec3 br = fetch_offset(tl, one ) * rcol * vec3_splat(bcol); - vec3 bl = fetch_offset(tl, vec2(0,one.y)) * lcol * vec3_splat(bcol); - vec3 ur = fetch_offset(tl, vec2(one.x,0)) * rcol * vec3_splat(tcol); - - vec3 csum = ul + br + bl + ur; - vec3 col = mul(mtxFromCols(u_rsubpix.rgb, u_gsubpix.rgb, u_bsubpix.rgb), csum); - gl_FragColor = vec4(pow(col, vec3_splat(1.0/u_monitorgamma.x)), 1.0); + vec3 cr = pow(u_rsubpix.rgb, vec3_splat(u_monitorgamma.x)); + vec3 cg = pow(u_gsubpix.rgb, vec3_splat(u_monitorgamma.x)); + vec3 cb = pow(u_bsubpix.rgb, vec3_splat(u_monitorgamma.x)); + + vec2 tl = v_texcoord0 * u_tex_size0.xy - vec2(0.5,0.5); + vec2 tli = floor(tl); + float subpix = fract(tl.x) * 3.0; + float rsubpix = u_tex_size0.x / u_quad_dims.x * 3.0; + + vec3 lcol, rcol; + lcol = vec3(intsmear_x(subpix+1.0, rsubpix, 3.0*u_subpixsize.x), + intsmear_x(subpix , rsubpix, 3.0*u_subpixsize.x), + intsmear_x(subpix-1.0, rsubpix, 3.0*u_subpixsize.x)); + rcol = vec3(intsmear_x(subpix-2.0, rsubpix, 3.0*u_subpixsize.x), + intsmear_x(subpix-3.0, rsubpix, 3.0*u_subpixsize.x), + intsmear_x(subpix-4.0, rsubpix, 3.0*u_subpixsize.x)); + + if (u_BGR.x > 0.5) { + lcol.rgb = lcol.bgr; + rcol.rgb = rcol.bgr; + } + + float tcol, bcol; + subpix = fract(tl.y); + rsubpix = u_tex_size0.y / u_quad_dims.y; + tcol = intsmear_y(subpix ,rsubpix, u_subpixsize.y); + bcol = intsmear_y(subpix-1.0,rsubpix, u_subpixsize.y); + + tl = (tli + vec2_splat(0.5)) / u_tex_size0.xy; + vec2 one = vec2_splat(1.0) / u_tex_size0.xy; + + vec3 ul = fetch_offset(tl, vec2(0,0) ) * lcol * vec3_splat(tcol); + vec3 br = fetch_offset(tl, one ) * rcol * vec3_splat(bcol); + vec3 bl = fetch_offset(tl, vec2(0,one.y)) * lcol * vec3_splat(bcol); + vec3 ur = fetch_offset(tl, vec2(one.x,0)) * rcol * vec3_splat(tcol); + + vec3 csum = ul + br + bl + ur; + vec3 col = mul(mtxFromCols(u_rsubpix.rgb, u_gsubpix.rgb, u_bsubpix.rgb), csum); + gl_FragColor = vec4(pow(col, vec3_splat(1.0/u_monitorgamma.x)), 1.0); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_persistence.sc b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_persistence.sc index 0187b8c90d2..4c9e998d3ec 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_persistence.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/fs_persistence.sc @@ -9,8 +9,8 @@ uniform vec4 u_persistence; void main() { - vec4 screen = texture2D(s_screen , v_texcoord0); - vec4 motionblur = texture2D(s_motionblur, v_texcoord0); + vec4 screen = texture2D(s_screen , v_texcoord0); + vec4 motionblur = texture2D(s_motionblur, v_texcoord0); - gl_FragColor = mix(screen, motionblur, u_persistence.x); + gl_FragColor = mix(screen, motionblur, u_persistence.x); } diff --git a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_lcd-grid.sc b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_lcd-grid.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_lcd-grid.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_lcd-grid.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_persistence.sc b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_persistence.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_persistence.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/lcd-grid/vs_persistence.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass0.sc index 3e58af492c6..090485da1bc 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass0.sc @@ -20,7 +20,7 @@ uniform vec4 DIFF_THRESH1; vec3 df3(vec3 c1, vec3 c2) { - return abs(c1 - c2); + return abs(c1 - c2); } bvec3 le3(vec3 A, vec3 B, vec3 param) diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass1.sc index 8fdd168a262..085f767358c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/fs_deposterize-pass1.sc @@ -20,7 +20,7 @@ uniform vec4 DIFF_THRESH2; vec3 df3(vec3 c1, vec3 c2) { - return abs(c1 - c2); + return abs(c1 - c2); } bvec3 le3(vec3 A, vec3 B, vec3 param) diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_blit.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_blit.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_blit.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_blit.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_bob-and-ghost-deinterlace.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_bob-and-ghost-deinterlace.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_bob-and-ghost-deinterlace.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_bob-and-ghost-deinterlace.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass0.sc index f46bf23b063..1ed054f1b9a 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass0.sc @@ -14,10 +14,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif vec2 ps = vec2(1.0 / u_tex_size0.xy); float dx = ps.x; diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass1.sc index 486edf97c5c..69a23cb477c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_deposterize-pass1.sc @@ -14,10 +14,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif vec2 ps = vec2(1.0 / u_tex_size0.xy); float dx = ps.x; diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_lut.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_lut.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_lut.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_lut.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_resize_blit.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_resize_blit.sc new file mode 100644 index 00000000000..0a9f2f178e1 --- /dev/null +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_resize_blit.sc @@ -0,0 +1,21 @@ +$input a_position, a_texcoord0, a_color0 +$output v_texcoord0, v_color0 + +// license:BSD-3-Clause +// copyright-holders:Dario Manesku + +#include "common.sh" + +uniform vec4 u_inv_view_dims; +uniform vec4 u_tex_bounds0; + +void main() +{ + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif + + v_texcoord0 = a_texcoord0 * (u_tex_bounds0.zw - u_tex_bounds0.xy) + u_tex_bounds0.xy; + v_color0 = a_color0; +} diff --git a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_saturation.sc b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_saturation.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/misc/vs_saturation.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/misc/vs_saturation.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_gaussian.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_gaussian.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_gaussian.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_gaussian.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_offset_sat.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_offset_sat.sc index f071beaaa26..77d392ad813 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_offset_sat.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_horizontal/vs_offset_sat.sc @@ -9,9 +9,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0 * vec2(0.16666666 * 0.9, 1.0 * 0.8) + vec2(0.1, 0.1); v_color0 = a_color0; -}
\ No newline at end of file +} diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_gaussian.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_gaussian.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_gaussian.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_gaussian.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_offset_sat.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_offset_sat.sc index a9f6649c1e5..6d47076f992 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_offset_sat.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_left_vertical/vs_offset_sat.sc @@ -9,9 +9,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0 * vec2(1.0 * 0.8, 0.685185 * 0.9) + vec2(0.1, 0.1); v_color0 = a_color0; -}
\ No newline at end of file +} diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_gaussian.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_gaussian.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_gaussian.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_gaussian.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_offset_sat.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_offset_sat.sc index a473bc4123c..403b880b5ec 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_offset_sat.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_horizontal/vs_offset_sat.sc @@ -9,9 +9,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0 * vec2(0.16666666 * 0.9, 1.0 * 0.8) + vec2(0.9 - 0.16666666 * 0.9, 0.1); v_color0 = a_color0; -}
\ No newline at end of file +} diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_gaussian.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_gaussian.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_gaussian.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_gaussian.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_offset_sat.sc b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_offset_sat.sc index 8af6a293d8e..7121ae6651c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_offset_sat.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/pillarbox_right_vertical/vs_offset_sat.sc @@ -9,9 +9,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { - gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); + gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0 * vec2(1.0 * 0.8, 0.685185 * 0.9) + vec2(0.1, 0.9 - 0.685185 * 0.9); v_color0 = a_color0; -}
\ No newline at end of file +} diff --git a/src/osd/modules/render/bgfx/shaders/chains/unfiltered/vs_blit.sc b/src/osd/modules/render/bgfx/shaders/chains/unfiltered/vs_blit.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/unfiltered/vs_blit.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/unfiltered/vs_blit.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/warp/vs_dilation-horizontal-fast.sc b/src/osd/modules/render/bgfx/shaders/chains/warp/vs_dilation-horizontal-fast.sc index 9433c8359be..52e0fc2ae90 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/warp/vs_dilation-horizontal-fast.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/warp/vs_dilation-horizontal-fast.sc @@ -32,10 +32,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; @@ -45,7 +49,7 @@ void main() // D E F // H - v_texcoord1 = vec4(1.0 / u_tex_size0.xy, 0.0, 0.0); // F H + v_texcoord1 = vec4(1.0 / u_tex_size0.xy, 0.0, 0.0); // F H v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_custom-jinc2-sharper.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_custom-jinc2-sharper.sc index fdf14bd393b..fd9a2d72f94 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_custom-jinc2-sharper.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_custom-jinc2-sharper.sc @@ -18,11 +18,11 @@ uniform vec4 u_tex_size0; SAMPLER2D(s_p, 0); - /* - This is an approximation of Jinc(x)*Jinc(x*r1/r2) for x < 2.5, - where r1 and r2 are the first two zeros of jinc function. - For a jinc 2-lobe best approximation, use A=0.5 and B=0.825. - */ +/* + This is an approximation of Jinc(x)*Jinc(x*r1/r2) for x < 2.5, + where r1 and r2 are the first two zeros of jinc function. + For a jinc 2-lobe best approximation, use A=0.5 and B=0.825. +*/ // A=0.5, B=0.825 is the best jinc approximation for x<2.5. if B=1.0, it's a lanczos filter. // Increase A to get more blur. Decrease it to get a sharper picture. @@ -47,12 +47,12 @@ float d(vec2 pt1, vec2 pt2) vec4 min4(vec4 a, vec4 b, vec4 c, vec4 d) { - return min(a, min(b, min(c, d))); + return min(a, min(b, min(c, d))); } vec4 max4(vec4 a, vec4 b, vec4 c, vec4 d) { - return max(a, max(b, max(c, d))); + return max(a, max(b, max(c, d))); } vec4 resampler(vec4 x) diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_super-2xbr-3d-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_super-2xbr-3d-pass0.sc index 9e5520740c2..fdb38420261 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_super-2xbr-3d-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/fs_super-2xbr-3d-pass0.sc @@ -95,23 +95,23 @@ float hv_wd(float i1, float i2, float i3, float i4, float e1, float e2, float e3 vec4 min4(vec4 a, vec4 b, vec4 c, vec4 d) { - return min(a, min(b, min(c, d))); + return min(a, min(b, min(c, d))); } vec4 max4(vec4 a, vec4 b, vec4 c, vec4 d) { - return max(a, max(b, max(c, d))); + return max(a, max(b, max(c, d))); } void main() { //Skip pixels on wrong grid vec2 fraction = fract(v_texcoord0 * u_tex_size0.xy / XBR_RES); - if (fraction.x < 0.5 || fraction.y < 0.5) - { - gl_FragColor = texture2D(s0, v_texcoord0); - return; - } + if (fraction.x < 0.5 || fraction.y < 0.5) + { + gl_FragColor = texture2D(s0, v_texcoord0); + return; + } vec2 tex = (floor(v_texcoord0 * u_tex_size0.xy / XBR_RES) + vec2(0.5, 0.5)) * XBR_RES / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_custom-jinc2-sharper.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_custom-jinc2-sharper.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_custom-jinc2-sharper.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_custom-jinc2-sharper.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass0.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass0.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass1.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass1.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass2.sc index 1c3ee6a8f23..335c9ef8b4c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-2xbr-3d-pass2.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_color #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass0.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass0.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1f.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1f.sc index c5bd41ee120..338cadbe4b7 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1f.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass1f.sc @@ -7,10 +7,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_color #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass2.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass2.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3f.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3f.sc index c5bd41ee120..338cadbe4b7 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3f.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-4xbr-3d-pass3f.sc @@ -7,10 +7,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_color #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass0.sc index 5827f1999b8..21e16b595e4 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass0.sc @@ -32,9 +32,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass1.sc index 60637684154..fc33723ec9d 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass1.sc @@ -32,9 +32,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass2.sc index 2cfe541e2b8..a8ec5539c0b 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-fast-pass2.sc @@ -32,9 +32,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass0.sc index d10c9c24665..8352137204e 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass0.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_color #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass1.sc index 5827f1999b8..21e16b595e4 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass1.sc @@ -32,9 +32,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass2.sc index 9ae87c8a7ea..8f2d3277665 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass2.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_color #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass3.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass3.sc index 9ae87c8a7ea..8f2d3277665 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass3.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/super-xbr/vs_super-xbr-pass3.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_color #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv1-noblend.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv1-noblend.sc index 3e97446a880..de23993d020 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv1-noblend.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv1-noblend.sc @@ -7,10 +7,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-3d.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-3d.sc index a3bb4330f45..7992b9eee51 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-3d.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-3d.sc @@ -9,10 +9,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco uniform vec4 XBR_RES; uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = XBR_RES.xx / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-fast.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-fast.sc index 38e92865308..48e9664b766 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-fast.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-fast.sc @@ -7,10 +7,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-noblend.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-noblend.sc index cfc1a7cfc47..5bcf3e31f5a 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-noblend.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2-noblend.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2.sc index ce6e411b75d..22e993426ed 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv2.sc @@ -7,10 +7,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3-noblend.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3-noblend.sc index 114f1669ad1..5957004443c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3-noblend.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3-noblend.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3.sc index 3d72da14478..a5ee8cee547 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/vs_xbr-lv3.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-sharp.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-sharp.sc index c09b7b66d1c..35cc742cff1 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-sharp.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-sharp.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2-gamma.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2-gamma.sc index 2be09e52908..3a9b0e56b1c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2-gamma.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2-gamma.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2.sc index 2ec42c6585d..c08e3988e24 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v2.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4-gamma.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4-gamma.sc index f40310aa1b9..8c2b0aec4cd 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4-gamma.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4-gamma.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4.sc index e363aba2eb4..d7328857ffd 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4b.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4b.sc index 8f9c58be4c1..48ebe4f0a54 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4b.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v4b.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v5-gamma.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v5-gamma.sc index f40310aa1b9..8c2b0aec4cd 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v5-gamma.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid-v5-gamma.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid.sc index c09b7b66d1c..35cc742cff1 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-hybrid/vs_2xbr-hybrid.sc @@ -43,10 +43,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; vec2 ps = 1.0 / u_tex_size0.xy; diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-a-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-a-pass0.sc index 59ffb0a026a..15490257390 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-a-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-a-pass0.sc @@ -34,10 +34,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass0.sc index 5c20062823a..f63eb7b2391 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass0.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass1.sc index fbb10ec9ea5..db8c513319b 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-accuracy-pass1.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_color0 #include "common.sh" uniform vec4 u_tex_size1; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-b-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-b-pass0.sc index afdcf872331..0c317f16cc7 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-b-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-b-pass0.sc @@ -34,10 +34,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-c-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-c-pass0.sc index ba0da15a70d..fbcf72fd706 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-c-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-c-pass0.sc @@ -34,10 +34,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-d-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-d-pass0.sc index bfd5e4a6414..e75cc78ac8c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-d-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-d-pass0.sc @@ -34,10 +34,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-noblend-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-noblend-pass1.sc index b85cca1828d..56cebbd6540 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-noblend-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-noblend-pass1.sc @@ -32,10 +32,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_color0 #include "common.sh" uniform vec4 u_tex_size1; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-pass1.sc index 11042eab09a..c35e62e81ac 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv2-multipass/vs_xbr-lv2-pass1.sc @@ -34,10 +34,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_color0 #include "common.sh" uniform vec4 u_tex_size1; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 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-lv3-multipass/vs_xbr-lv3-pass0.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass0.sc index 80dd11d7ad9..8ae8f8df41c 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass0.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass0.sc @@ -34,10 +34,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass1.sc index 7932ba25f15..439005d2941 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-lv3-multipass/vs_xbr-lv3-pass1.sc @@ -32,10 +32,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_color0 uniform vec4 u_tex_size1; uniform vec4 u_target_dims; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A3 B3 C3 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/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass1.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass1.sc index 1edfd8632ad..24f3b396cc2 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass1.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass1.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A3 B3 C3 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass2.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass2.sc index 92cf6b60b87..1d67bb87ebe 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass2.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass2.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3, v_texcoord4, v_texco #include "common.sh" uniform vec4 u_tex_size0; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A1 B1 C1 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass3.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass3.sc index a6926a41f8b..2c85859340b 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass3.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass3.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size1; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A3 B3 C3 diff --git a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass4.sc b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass4.sc index 29fec2f1759..5e62a75a12e 100644 --- a/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass4.sc +++ b/src/osd/modules/render/bgfx/shaders/chains/xbr/xbr-mlv4-multipass/vs_xbr-mlv4-pass4.sc @@ -33,10 +33,14 @@ $output v_texcoord0, v_texcoord1, v_color0 #include "common.sh" uniform vec4 u_tex_size1; +uniform vec4 u_inv_view_dims; void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; // A3 B3 C3 diff --git a/src/osd/modules/render/bgfx/shaders/vs_gui.sc b/src/osd/modules/render/bgfx/shaders/vs_gui.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/vs_gui.sc +++ b/src/osd/modules/render/bgfx/shaders/vs_gui.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/shaders/vs_screen.sc b/src/osd/modules/render/bgfx/shaders/vs_screen.sc index 405ef8feb3b..df2b37f203c 100644 --- a/src/osd/modules/render/bgfx/shaders/vs_screen.sc +++ b/src/osd/modules/render/bgfx/shaders/vs_screen.sc @@ -6,9 +6,14 @@ $output v_texcoord0, v_color0 #include "common.sh" +uniform vec4 u_inv_view_dims; + void main() { gl_Position = mul(u_viewProj, vec4(a_position.xy, 0.0, 1.0)); +#if BGFX_SHADER_LANGUAGE_HLSL && BGFX_SHADER_LANGUAGE_HLSL <= 300 + gl_Position.xy += u_inv_view_dims.xy * gl_Position.w; +#endif v_texcoord0 = a_texcoord0; v_color0 = a_color0; } diff --git a/src/osd/modules/render/bgfx/slider.cpp b/src/osd/modules/render/bgfx/slider.cpp index 452c934fd37..dcd64c0c865 100644 --- a/src/osd/modules/render/bgfx/slider.cpp +++ b/src/osd/modules/render/bgfx/slider.cpp @@ -11,8 +11,10 @@ #include "strformat.h" -bgfx_slider::bgfx_slider(running_machine &machine, std::string name, float min, float def, float max, float step, slider_type type, screen_type screen, std::string format, std::string description, std::vector<std::string>& strings) - : m_name(name) +#include <utility> + +bgfx_slider::bgfx_slider(running_machine &machine, std::string &&name, float min, float def, float max, float step, slider_type type, screen_type screen, std::string format, std::string description, std::vector<std::string>& strings) + : m_name(std::move(name)) , m_step(step) , m_type(type) , m_screen_type(screen) diff --git a/src/osd/modules/render/bgfx/slider.h b/src/osd/modules/render/bgfx/slider.h index d7815765c34..89a713da4b3 100644 --- a/src/osd/modules/render/bgfx/slider.h +++ b/src/osd/modules/render/bgfx/slider.h @@ -6,14 +6,15 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_SLIDER_H +#define MAME_RENDER_BGFX_SLIDER_H -#ifndef __DRAWBGFX_SLIDER__ -#define __DRAWBGFX_SLIDER__ +#pragma once #include <bgfx/bgfx.h> #include <cmath> +#include <cstdint> #include <memory> #include <string> #include <vector> @@ -46,16 +47,19 @@ public: SLIDER_SCREEN_TYPE_ANY = SLIDER_SCREEN_TYPE_RASTER | SLIDER_SCREEN_TYPE_VECTOR | SLIDER_SCREEN_TYPE_LCD }; - bgfx_slider(running_machine& machine, std::string name, float min, float def, float max, float step, slider_type type, screen_type screen, std::string format, std::string description, std::vector<std::string>& strings); + bgfx_slider(running_machine& machine, std::string &&name, float min, float def, float max, float step, slider_type type, screen_type screen, std::string format, std::string description, std::vector<std::string>& strings); virtual ~bgfx_slider(); int32_t update(std::string *str, int32_t newval); // Getters - std::string name() const { return m_name; } + const std::string &name() const { return m_name; } slider_type type() const { return m_type; } float value() const { return m_value; } float uniform_value() const { return float(m_value); } + float min_value() const { return m_min; } + float default_value() const { return m_default; } + float max_value() const { return m_max; } slider_state *core_slider() const { return m_slider_state.get(); } size_t size() const { return get_size_for_type(m_type); } static size_t get_size_for_type(slider_type type); @@ -82,4 +86,4 @@ protected: running_machine&m_machine; }; -#endif // __DRAWBGFX_SLIDER__ +#endif // MAME_RENDER_BGFX_SLIDER_H diff --git a/src/osd/modules/render/bgfx/sliderreader.cpp b/src/osd/modules/render/bgfx/sliderreader.cpp index 208bc241559..96ff2511c96 100644 --- a/src/osd/modules/render/bgfx/sliderreader.cpp +++ b/src/osd/modules/render/bgfx/sliderreader.cpp @@ -33,7 +33,7 @@ const slider_reader::string_to_enum slider_reader::SCREEN_NAMES[slider_reader::S { "all", uint64_t(bgfx_slider::screen_type::SLIDER_SCREEN_TYPE_ANY) } }; -std::vector<bgfx_slider*> slider_reader::read_from_value(const Value& value, std::string prefix, chain_manager& chains, uint32_t screen_index) +std::vector<bgfx_slider*> slider_reader::read_from_value(const Value& value, const std::string &prefix, chain_manager& chains, uint32_t screen_index) { std::vector<bgfx_slider*> sliders; @@ -55,7 +55,7 @@ std::vector<bgfx_slider*> slider_reader::read_from_value(const Value& value, std const Value& string_array = value["strings"]; for (uint32_t i = 0; i < string_array.Size(); i++) { - if (!READER_CHECK(string_array[i].IsString(), (prefix + "Slider '" + name + "': strings[" + std::to_string(i) + "]: must be a string\n").c_str())) + if (!READER_CHECK(string_array[i].IsString(), "%sSlider '%s': strings[%u]: must be a string\n", prefix, name, i)) { return sliders; } @@ -82,18 +82,22 @@ std::vector<bgfx_slider*> slider_reader::read_from_value(const Value& value, std break; } - std::string prefixed_desc = "Window " + std::to_string(chains.window_index()) + ", Screen " + std::to_string(screen_index) + ", " + description; + std::string prefixed_desc = util::string_format("Window %1$u, Screen %2$u, %3$s", chains.window_index(), screen_index, description); if (slider_count > 1) { + if (!READER_CHECK(value["min"].IsArray(), "%1$sSlider '%2$s': value 'min' must be an array", prefix, name)) + return sliders; + if (!READER_CHECK(value["default"].IsArray(), "%1$sSlider '%2$s': value 'default' must be an array", prefix, name)) + return sliders; + if (!READER_CHECK(value["max"].IsArray(), "%1$sSlider '%2$s': value 'max' must be an array", prefix, name)) + return sliders; + float min[3]; float defaults[3]; float max[3]; - if (!READER_CHECK(value["min"].IsArray(), (prefix + "Slider '" + name + "': value 'min' must be an array\n").c_str())) return sliders; - if (!READER_CHECK(value["default"].IsArray(), (prefix + "Slider '" + name + "': value 'default' must be an array\n").c_str())) return sliders; - if (!READER_CHECK(value["max"].IsArray(), (prefix + "Slider '" + name + "': value 'max' must be an array\n").c_str())) return sliders; - get_values(value, prefix + "Slider '" + name + "': 'min': ", "min", min, slider_count); - get_values(value, prefix + "Slider '" + name + "': 'default': ", "default", defaults, slider_count); - get_values(value, prefix + "Slider '" + name + "': 'max': ", "max", max, slider_count); + get_values(value, util::string_format("%1$sSlider '%2$s': 'min': ", prefix, name), "min", min, slider_count); + get_values(value, util::string_format("%1$sSlider '%2$s': 'default': ", prefix, name), "default", defaults, slider_count); + get_values(value, util::string_format("%1$sSlider '%2$s': 'max': ", prefix, name), "max", max, slider_count); for (int index = 0; index < slider_count; index++) { std::string desc; @@ -113,51 +117,50 @@ std::vector<bgfx_slider*> slider_reader::read_from_value(const Value& value, std desc = prefixed_desc + "Invalid"; break; } - sliders.push_back(new bgfx_slider(chains.machine(), full_name, min[index], defaults[index], max[index], step, type, screen_type, format, desc, strings)); + sliders.push_back(new bgfx_slider(chains.machine(), std::move(full_name), min[index], defaults[index], max[index], step, type, screen_type, format, desc, strings)); } } else { - float min = get_float(value, "min", 0.0f); - float def = get_float(value, "default", 0.0f); - float max = get_float(value, "max", 1.0f); + const float min = get_float(value, "min", 0.0f); + const float def = get_float(value, "default", 0.0f); + const float max = get_float(value, "max", 1.0f); sliders.push_back(new bgfx_slider(chains.machine(), name + "0", min, def, max, step, type, screen_type, format, prefixed_desc, strings)); } return sliders; } -bool slider_reader::get_values(const Value& value, std::string prefix, std::string name, float* values, const int count) +bool slider_reader::get_values(const Value& value, const std::string &prefix, const std::string &name, float* values, const int count) { - const char* name_str = name.c_str(); - const Value& value_array = value[name_str]; + const Value& value_array = value[name.c_str()]; for (uint32_t i = 0; i < value_array.Size() && i < count; i++) { - if (!READER_CHECK(value_array[i].IsNumber(), (prefix + "Entry " + std::to_string(i) + " must be a number\n").c_str())) return false; + if (!READER_CHECK(value_array[i].IsNumber(), "%sEntry %u must be a number\n", prefix, i)) return false; values[i] = value_array[i].GetFloat(); } return true; } -bool slider_reader::validate_parameters(const Value& value, std::string prefix) +bool slider_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'name'\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("min"), (prefix + "Must have a number or array value 'min'\n").c_str())) return false; - if (!READER_CHECK(value["min"].IsNumber() || value["min"].IsArray(), (prefix + "Value 'min' must be a number or an array the size of the corresponding slider type\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("default"), (prefix + "Must have a number or array value 'default'\n").c_str())) return false; - if (!READER_CHECK(value["default"].IsNumber() || value["default"].IsArray(), (prefix + "Value 'default' must be a number or an array the size of the corresponding slider type\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("max"), (prefix + "Must have a number or array value 'max'\n").c_str())) return false; - if (!READER_CHECK(value["max"].IsNumber() || value["max"].IsArray(), (prefix + "Value 'max' must be a number or an array the size of the corresponding slider type\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("step"), (prefix + "Must have a number value 'step'\n").c_str())) return false; - if (!READER_CHECK(value["step"].IsNumber(), (prefix + "Value 'step' must be a number (how much does this slider increment by internally?)\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("type"), (prefix + "Must have string value 'type'\n").c_str())) return false; - if (!READER_CHECK(value["type"].IsString(), (prefix + "Value 'type' must be a string (what type of slider is this? [int_enum, int, float])\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("screen"), (prefix + "Must have string value 'screen'\n").c_str())) return false; - if (!READER_CHECK(value["screen"].IsString(), (prefix + "Value 'screen' must be a string (what type of output device does this slider apply to? [none, raster, vector, crt, lcd, non_vector, any])\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("format"), (prefix + "Must have string value 'format'\n").c_str())) return false; - if (!READER_CHECK(value["format"].IsString(), (prefix + "Value 'scale' must be a string (how would we display it in a printf?)").c_str())) return false; - if (!READER_CHECK(value.HasMember("text"), (prefix + "Must have string value 'text'\n").c_str())) return false; - if (!READER_CHECK(value["text"].IsString(), (prefix + "Value 'text' must be a string (how would you explain it?)").c_str())) return false; - if (!READER_CHECK(!value.HasMember("strings") || value["strings"].IsArray(), (prefix + "Value 'strings' must be an array\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("name"), "%1$sMust have string value 'name'", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%1$sValue 'name' must be a string", prefix)) return false; + if (!READER_CHECK(value.HasMember("min"), "%1$sMust have a number or array value 'min'", prefix)) return false; + if (!READER_CHECK(value["min"].IsNumber() || value["min"].IsArray(), "%1$sValue 'min' must be a number or an array the size of the corresponding slider type", prefix)) return false; + if (!READER_CHECK(value.HasMember("default"), "%1$sMust have a number or array value 'default'", prefix)) return false; + if (!READER_CHECK(value["default"].IsNumber() || value["default"].IsArray(), "%1$sValue 'default' must be a number or an array the size of the corresponding slider type", prefix)) return false; + if (!READER_CHECK(value.HasMember("max"), "%1$sMust have a number or array value 'max'", prefix)) return false; + if (!READER_CHECK(value["max"].IsNumber() || value["max"].IsArray(), "%1$sValue 'max' must be a number or an array the size of the corresponding slider type", prefix)) return false; + if (!READER_CHECK(value.HasMember("step"), "%1$sMust have a number value 'step'", prefix)) return false; + if (!READER_CHECK(value["step"].IsNumber(), "%1$sValue 'step' must be a number (how much does this slider increment by internally?)", prefix)) return false; + if (!READER_CHECK(value.HasMember("type"), "%1$sMust have string value 'type'", prefix)) return false; + if (!READER_CHECK(value["type"].IsString(), "%1$sValue 'type' must be a string (what type of slider is this? [int_enum, int, float])", prefix)) return false; + if (!READER_CHECK(value.HasMember("screen"), "%1$sMust have string value 'screen'", prefix)) return false; + if (!READER_CHECK(value["screen"].IsString(), "%1$sValue 'screen' must be a string (what type of output device does this slider apply to? [none, raster, vector, crt, lcd, non_vector, any])", prefix)) return false; + if (!READER_CHECK(value.HasMember("format"), "%1$sMust have string value 'format'", prefix)) return false; + if (!READER_CHECK(value["format"].IsString(), "%1$sValue 'scale' must be a string (how would we display it in a printf?)", prefix)) return false; + if (!READER_CHECK(value.HasMember("text"), "%1$sMust have string value 'text'", prefix)) return false; + if (!READER_CHECK(value["text"].IsString(), "%1$sValue 'text' must be a string (how would you explain it?)", prefix)) return false; + if (!READER_CHECK(!value.HasMember("strings") || value["strings"].IsArray(), "%1$sValue 'strings' must be an array", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/sliderreader.h b/src/osd/modules/render/bgfx/sliderreader.h index 95c6872e171..dc905472cc8 100644 --- a/src/osd/modules/render/bgfx/sliderreader.h +++ b/src/osd/modules/render/bgfx/sliderreader.h @@ -6,26 +6,26 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_SLIDERREADER_H +#define MAME_RENDER_BGFX_SLIDERREADER_H + #pragma once -#ifndef __DRAWBGFX_SLIDER_READER__ -#define __DRAWBGFX_SLIDER_READER__ +#include "statereader.h" #include <vector> -#include "statereader.h" - class bgfx_slider; class chain_manager; class slider_reader : public state_reader { public: - static std::vector<bgfx_slider*> read_from_value(const Value& value, std::string prefix, chain_manager& chains, uint32_t screen_index); + static std::vector<bgfx_slider*> read_from_value(const Value& value, const std::string &prefix, chain_manager& chains, uint32_t screen_index); private: - static bool get_values(const Value& value, std::string prefix, std::string name, float* values, const int count); - static bool validate_parameters(const Value& value, std::string prefix); + static bool get_values(const Value& value, const std::string &prefix, const std::string &name, float* values, const int count); + static bool validate_parameters(const Value& value, const std::string &prefix); static const int TYPE_COUNT = 5; static const string_to_enum TYPE_NAMES[TYPE_COUNT]; @@ -33,4 +33,4 @@ private: static const string_to_enum SCREEN_NAMES[SCREEN_COUNT]; }; -#endif // __DRAWBGFX_SLIDER_READER__ +#endif // MAME_RENDER_BGFX_SLIDERREADER_H diff --git a/src/osd/modules/render/bgfx/slideruniform.cpp b/src/osd/modules/render/bgfx/slideruniform.cpp index 7a63b1e6901..bc116f51ff1 100644 --- a/src/osd/modules/render/bgfx/slideruniform.cpp +++ b/src/osd/modules/render/bgfx/slideruniform.cpp @@ -13,13 +13,12 @@ #include "slider.h" -bgfx_slider_uniform::bgfx_slider_uniform(bgfx_uniform* uniform, std::vector<bgfx_slider*> sliders) +#include <utility> + +bgfx_slider_uniform::bgfx_slider_uniform(bgfx_uniform* uniform, std::vector<bgfx_slider*> &&sliders) : bgfx_entry_uniform(uniform) + , m_sliders(std::move(sliders)) { - for (bgfx_slider* slider : sliders) - { - m_sliders.push_back(slider); - } } void bgfx_slider_uniform::bind() diff --git a/src/osd/modules/render/bgfx/slideruniform.h b/src/osd/modules/render/bgfx/slideruniform.h index a2fa990dd1f..a08d2fb503d 100644 --- a/src/osd/modules/render/bgfx/slideruniform.h +++ b/src/osd/modules/render/bgfx/slideruniform.h @@ -9,10 +9,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_SLIDERUNIFORM_H +#define MAME_RENDER_BGFX_SLIDERUNIFORM_H -#ifndef __DRAWBGFX_SLIDER_UNIFORM__ -#define __DRAWBGFX_SLIDER_UNIFORM__ +#pragma once #include "entryuniform.h" @@ -23,7 +23,7 @@ class bgfx_slider; class bgfx_slider_uniform : public bgfx_entry_uniform { public: - bgfx_slider_uniform(bgfx_uniform* uniform, std::vector<bgfx_slider*> sliders); + bgfx_slider_uniform(bgfx_uniform* uniform, std::vector<bgfx_slider*> &&sliders); virtual void bind() override; @@ -31,4 +31,4 @@ private: std::vector<bgfx_slider*> m_sliders; }; -#endif // __DRAWBGFX_SLIDER_UNIFORM__ +#endif // MAME_RENDER_BGFX_SLIDERUNIFORM_H diff --git a/src/osd/modules/render/bgfx/slideruniformreader.cpp b/src/osd/modules/render/bgfx/slideruniformreader.cpp index 314cff0b78b..3fdeb0b21e6 100644 --- a/src/osd/modules/render/bgfx/slideruniformreader.cpp +++ b/src/osd/modules/render/bgfx/slideruniformreader.cpp @@ -8,13 +8,13 @@ #include "slideruniformreader.h" -#include <vector> - -#include "slideruniform.h" #include "entryuniform.h" #include "slider.h" +#include "slideruniform.h" + +#include <vector> -bgfx_entry_uniform* slider_uniform_reader::read_from_value(const Value& value, std::string prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_slider*>& sliders) +bgfx_entry_uniform* slider_uniform_reader::read_from_value(const Value& value, const std::string &prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_slider*>& sliders) { if (!validate_parameters(value, prefix)) { @@ -35,12 +35,12 @@ bgfx_entry_uniform* slider_uniform_reader::read_from_value(const Value& value, s slider_list.push_back(sliders[name + "2"]); } - return new bgfx_slider_uniform(uniform, slider_list); + return new bgfx_slider_uniform(uniform, std::move(slider_list)); } -bool slider_uniform_reader::validate_parameters(const Value& value, std::string prefix) +bool slider_uniform_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("slider"), (prefix + "Must have string value 'slider' (what slider are we getting the value of?)\n").c_str())) return false; - if (!READER_CHECK(value["slider"].IsString(), (prefix + "Value 'slider' must be a string\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("slider"), "%sMust have string value 'slider' (what slider are we getting the value of?)\n", prefix)) return false; + if (!READER_CHECK(value["slider"].IsString(), "%sValue 'slider' must be a string\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/slideruniformreader.h b/src/osd/modules/render/bgfx/slideruniformreader.h index 0db387270b0..f39e7fc2fd8 100644 --- a/src/osd/modules/render/bgfx/slideruniformreader.h +++ b/src/osd/modules/render/bgfx/slideruniformreader.h @@ -6,27 +6,27 @@ // //================================================================ +#ifndef MAME_RENDER_BGFX_SLIDERUNIFORMREADER_H +#define MAME_RENDER_BGFX_SLIDERUNIFORMREADER_H + #pragma once -#ifndef __DRAWBGFX_SLIDER_UNIFORM_READER__ -#define __DRAWBGFX_SLIDER_UNIFORM_READER__ +#include "entryuniform.h" +#include "statereader.h" #include <string> #include <map> -#include "entryuniform.h" -#include "statereader.h" - class bgfx_uniform; class bgfx_slider; class slider_uniform_reader : public state_reader { public: - static bgfx_entry_uniform* read_from_value(const Value& value, std::string prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_slider*>& sliders); + static bgfx_entry_uniform* read_from_value(const Value& value, const std::string &prefix, bgfx_uniform* uniform, std::map<std::string, bgfx_slider*>& sliders); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_SLIDER_UNIFORM_READER__ +#endif // MAME_RENDER_BGFX_SLIDERUNIFORMREADER_H diff --git a/src/osd/modules/render/bgfx/statereader.cpp b/src/osd/modules/render/bgfx/statereader.cpp index acaeddd35b5..277e6815185 100644 --- a/src/osd/modules/render/bgfx/statereader.cpp +++ b/src/osd/modules/render/bgfx/statereader.cpp @@ -13,17 +13,20 @@ #include <cmath> -bool state_reader::READER_CHECK(bool condition, const char* format, ...) + +bool state_reader::V_READER_CHECK(bool condition, const util::format_argument_pack<char> &args) { if (!condition) - { - va_list ap; - va_start(ap, format); - char buf[2048]; - vsnprintf(buf, 2048, format, ap); - osd_printf_error("Error: %s\n", buf); - va_end(ap); - } + osd_printf_error("Error: %s\n", util::string_format(args)); + + return condition; +} + +bool state_reader::V_READER_WARN(bool condition, const util::format_argument_pack<char> &args) +{ + if (!condition) + osd_printf_warning("Warning: %s\n", util::string_format(args)); + return condition; } @@ -117,7 +120,7 @@ float state_reader::get_float(const Value& value, const std::string name, const { if (value.HasMember(name.c_str())) { - return (float)value[name.c_str()].GetDouble(); + return float(value[name.c_str()].GetDouble()); } return default_value; } @@ -128,7 +131,7 @@ void state_reader::get_float(const Value& value, const std::string name, float* { if (count == 1) { - *out = (float) value[name.c_str()].GetDouble(); + *out = float(value[name.c_str()].GetDouble()); return; } else @@ -146,7 +149,7 @@ void state_reader::get_vec_values(const Value& value_array, float* data, const u { for (unsigned int i = 0; i < count && i < value_array.Size(); i++) { - data[i] = (float) value_array[i].GetDouble(); + data[i] = float(value_array[i].GetDouble()); } } diff --git a/src/osd/modules/render/bgfx/statereader.h b/src/osd/modules/render/bgfx/statereader.h index 43596bf4e94..69a795d9f04 100644 --- a/src/osd/modules/render/bgfx/statereader.h +++ b/src/osd/modules/render/bgfx/statereader.h @@ -7,16 +7,19 @@ // //================================================================ +#ifndef MAME_RENDER_BGFX_STATEREADER_H +#define MAME_RENDER_BGFX_STATEREADER_H + #pragma once -#ifndef DRAWBGFX_STATE_READER -#define DRAWBGFX_STATE_READER +#include "util/strformat.h" #include <rapidjson/document.h> #include <cstdint> #include <string> + using namespace rapidjson; class state_reader @@ -44,10 +47,22 @@ protected: static uint64_t get_param_from_string(std::string value, const string_to_enum* enums, const int count); protected: - static bool READER_CHECK(bool condition, const char* format, ...); + template <typename Format, typename... Params> + static bool READER_CHECK(bool condition, Format &&fmt, Params &&... args) + { + return V_READER_CHECK(condition, util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); + } + + template <typename Format, typename... Params> + static bool READER_WARN(bool condition, Format &&fmt, Params &&... args) + { + return V_READER_WARN(condition, util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...)); + } private: + static bool V_READER_CHECK(bool condition, const util::format_argument_pack<char> &args); + static bool V_READER_WARN(bool condition, const util::format_argument_pack<char> &args); static void get_vec_values(const Value& value_array, float* data, const unsigned int count); }; -#endif // DRAWBGFX_STATE_READER +#endif // MAME_RENDER_BGFX_STATEREADER_H diff --git a/src/osd/modules/render/bgfx/suppressor.cpp b/src/osd/modules/render/bgfx/suppressor.cpp index 83dd403adf7..8ffb4be6d66 100644 --- a/src/osd/modules/render/bgfx/suppressor.cpp +++ b/src/osd/modules/render/bgfx/suppressor.cpp @@ -13,13 +13,13 @@ #include <cstring> -bgfx_suppressor::bgfx_suppressor(std::vector<bgfx_slider*> sliders, uint32_t condition, combine_mode combine, void* value) - : m_sliders(sliders) +bgfx_suppressor::bgfx_suppressor(std::vector<bgfx_slider*> &&sliders, uint32_t condition, combine_mode combine, void* value) + : m_sliders(std::move(sliders)) , m_condition(condition) , m_combine(combine) , m_value(nullptr) { - uint32_t size = sliders[0]->size(); + uint32_t size = m_sliders[0]->size(); m_value = new uint8_t[size]; memcpy(m_value, value, size); } diff --git a/src/osd/modules/render/bgfx/suppressor.h b/src/osd/modules/render/bgfx/suppressor.h index 343a621b72d..00276fbb398 100644 --- a/src/osd/modules/render/bgfx/suppressor.h +++ b/src/osd/modules/render/bgfx/suppressor.h @@ -7,10 +7,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_SUPPRESSOR_H +#define MAME_RENDER_BGFX_SUPPRESSOR_H -#ifndef __DRAWBGFX_SUPPRESSOR__ -#define __DRAWBGFX_SUPPRESSOR__ +#pragma once #include <bgfx/bgfx.h> @@ -34,7 +34,7 @@ public: COMBINE_OR }; - bgfx_suppressor(std::vector<bgfx_slider*> sliders, uint32_t condition, combine_mode combine, void* value); + bgfx_suppressor(std::vector<bgfx_slider*> &&sliders, uint32_t condition, combine_mode combine, void* value); ~bgfx_suppressor(); // Getters @@ -48,4 +48,4 @@ private: uint8_t* m_value; }; -#endif // __DRAWBGFX_SUPPRESSOR__ +#endif // MAME_RENDER_BGFX_SUPPRESSOR_H diff --git a/src/osd/modules/render/bgfx/suppressorreader.cpp b/src/osd/modules/render/bgfx/suppressorreader.cpp index d7eb2b21fb8..353b79ad9bf 100644 --- a/src/osd/modules/render/bgfx/suppressorreader.cpp +++ b/src/osd/modules/render/bgfx/suppressorreader.cpp @@ -21,7 +21,7 @@ const suppressor_reader::string_to_enum suppressor_reader::COMBINE_NAMES[suppres { "or", bgfx_suppressor::combine_mode::COMBINE_OR } }; -bgfx_suppressor* suppressor_reader::read_from_value(const Value& value, std::string prefix, std::map<std::string, bgfx_slider*>& sliders) +bgfx_suppressor* suppressor_reader::read_from_value(const Value& value, const std::string &prefix, std::map<std::string, bgfx_slider*>& sliders) { if (!validate_parameters(value, prefix)) { @@ -58,7 +58,7 @@ bgfx_suppressor* suppressor_reader::read_from_value(const Value& value, std::str if (slider_count > 1) { get_values(value, prefix, "value", values, slider_count); - if (!READER_CHECK(slider_count == value["value"].GetArray().Size(), (prefix + "Expected " + std::to_string(slider_count) + " values, got " + std::to_string(value["value"].GetArray().Size()) + "\n").c_str())) return nullptr; + if (!READER_CHECK(slider_count == value["value"].GetArray().Size(), "%sExpected %d values, got %u\n", prefix, slider_count, value["value"].GetArray().Size())) return nullptr; for (int index = 1; index < slider_count; index++) { check_sliders.push_back(sliders[name + std::to_string(index)]); @@ -69,26 +69,25 @@ bgfx_suppressor* suppressor_reader::read_from_value(const Value& value, std::str values[0] = get_int(value, "value", 0); } - return new bgfx_suppressor(check_sliders, condition, mode, values); + return new bgfx_suppressor(std::move(check_sliders), condition, mode, values); } -bool suppressor_reader::validate_parameters(const Value& value, std::string prefix) +bool suppressor_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value["type"].IsString(), (prefix + "Value 'type' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'name'\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("value"), (prefix + "Must have numeric or array value 'value'\n").c_str())) return false; - if (!READER_CHECK(value["value"].IsNumber() || value["value"].IsArray(), (prefix + "Value 'value' must be a number or array the size of the corresponding slider type\n").c_str())) return false; + if (!READER_CHECK(value["type"].IsString(), "%sValue 'type' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("name"), "%sMust have string value 'name'\n", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%sValue 'name' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("value"), "%sMust have numeric or array value 'value'\n", prefix)) return false; + if (!READER_CHECK(value["value"].IsNumber() || value["value"].IsArray(), "%sValue 'value' must be a number or array the size of the corresponding slider type\n", prefix)) return false; return true; } bool suppressor_reader::get_values(const Value& value, std::string prefix, std::string name, int* values, const int count) { - const char* name_str = name.c_str(); - const Value& value_array = value[name_str]; + const Value& value_array = value[name.c_str()]; for (uint32_t i = 0; i < value_array.Size() && i < count; i++) { - if (!READER_CHECK(value_array[i].IsInt(), (prefix + "value[" + std::to_string(i) + "] must be an integer\n").c_str())) return false; + if (!READER_CHECK(value_array[i].IsInt(), "%svalue[%u] must be an integer\n", prefix, i)) return false; values[i] = value_array[i].GetInt(); } return true; diff --git a/src/osd/modules/render/bgfx/suppressorreader.h b/src/osd/modules/render/bgfx/suppressorreader.h index cf7d641997c..23855134c51 100644 --- a/src/osd/modules/render/bgfx/suppressorreader.h +++ b/src/osd/modules/render/bgfx/suppressorreader.h @@ -6,10 +6,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_SUPPRESSORREADER_H +#define MAME_RENDER_BGFX_SUPPRESSORREADER_H -#ifndef __DRAWBGFX_SUPPRESSOR_READER__ -#define __DRAWBGFX_SUPPRESSOR_READER__ +#pragma once #include "statereader.h" @@ -22,11 +22,11 @@ class bgfx_slider; class suppressor_reader : public state_reader { public: - static bgfx_suppressor* read_from_value(const Value& value, std::string prefix, std::map<std::string, bgfx_slider*>& sliders); + static bgfx_suppressor* read_from_value(const Value& value, const std::string &prefix, std::map<std::string, bgfx_slider*>& sliders); private: static bool get_values(const Value& value, std::string prefix, std::string name, int* values, const int count); - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); static const int CONDITION_COUNT = 2; static const string_to_enum CONDITION_NAMES[CONDITION_COUNT]; @@ -34,4 +34,4 @@ private: static const string_to_enum COMBINE_NAMES[COMBINE_COUNT]; }; -#endif // __DRAWBGFX_SUPPRESSOR_READER__ +#endif // MAME_RENDER_BGFX_SUPPRESSORREADER_H diff --git a/src/osd/modules/render/bgfx/target.cpp b/src/osd/modules/render/bgfx/target.cpp index f900f4f99fb..dd9169cb6cd 100644 --- a/src/osd/modules/render/bgfx/target.cpp +++ b/src/osd/modules/render/bgfx/target.cpp @@ -10,13 +10,16 @@ #include "target.h" -bgfx_target::bgfx_target(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen) +bgfx_target::bgfx_target(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint16_t xprescale, uint16_t yprescale, + uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen) : m_name(name) , m_format(format) , m_targets(nullptr) , m_textures(nullptr) , m_width(width) , m_height(height) + , m_xprescale(xprescale) + , m_yprescale(yprescale) , m_double_buffer(double_buffer) , m_style(style) , m_filter(filter) @@ -39,10 +42,10 @@ bgfx_target::bgfx_target(std::string name, bgfx::TextureFormat::Enum format, uin m_targets = new bgfx::FrameBufferHandle[m_page_count]; for (int page = 0; page < m_page_count; page++) { - m_textures[page] = bgfx::createTexture2D(m_width, m_height, false, 1, format, wrap_mode | filter_mode | BGFX_TEXTURE_RT); + m_textures[page] = bgfx::createTexture2D(m_width * xprescale, m_height * yprescale, false, 1, format, wrap_mode | filter_mode | BGFX_TEXTURE_RT); assert(m_textures[page].idx != 0xffff); - m_textures[m_page_count + page] = bgfx::createTexture2D(m_width, m_height, false, 1, bgfx::TextureFormat::D32F, depth_flags | BGFX_TEXTURE_RT); + m_textures[m_page_count + page] = bgfx::createTexture2D(m_width * xprescale, m_height * yprescale, false, 1, bgfx::TextureFormat::D32F, depth_flags | BGFX_TEXTURE_RT); assert(m_textures[m_page_count + page].idx != 0xffff); bgfx::TextureHandle handles[2] = { m_textures[page], m_textures[m_page_count + page] }; @@ -62,6 +65,8 @@ bgfx_target::bgfx_target(void *handle, uint16_t width, uint16_t height) , m_textures(nullptr) , m_width(width) , m_height(height) + , m_xprescale(1) + , m_yprescale(1) , m_double_buffer(false) , m_style(TARGET_STYLE_CUSTOM) , m_filter(false) diff --git a/src/osd/modules/render/bgfx/target.h b/src/osd/modules/render/bgfx/target.h index c00fc669337..5b979373971 100644 --- a/src/osd/modules/render/bgfx/target.h +++ b/src/osd/modules/render/bgfx/target.h @@ -27,7 +27,8 @@ enum class bgfx_target : public bgfx_texture_handle_provider { public: - bgfx_target(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen); + bgfx_target(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint16_t xprescale, uint16_t yprescale, + uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen); bgfx_target(void *handle, uint16_t width, uint16_t height); virtual ~bgfx_target(); @@ -42,13 +43,16 @@ public: bool filter() const { return m_filter; } uint16_t scale() const { return m_scale; } uint32_t screen_index() const { return m_screen; } + uint16_t raw_width() const { return m_width; } + uint16_t raw_height() const { return m_height; } // bgfx_texture_handle_provider virtual bgfx::TextureHandle texture() const override; virtual bool is_target() const override { return true; } - virtual uint16_t width() const override { return m_width; } - virtual uint16_t height() const override { return m_height; } - virtual uint16_t rowpixels() const override { return m_width; } + virtual uint16_t width() const override { return m_width * m_xprescale; } + virtual uint16_t width_margin() const override { return 0; } + virtual uint16_t height() const override { return m_height * m_yprescale; } + virtual uint16_t rowpixels() const override { return m_width * m_xprescale; } virtual int width_div_factor() const override { return 1; } virtual int width_mul_factor() const override { return 1; } @@ -62,6 +66,8 @@ private: uint16_t m_width; uint16_t m_height; + uint16_t m_xprescale; + uint16_t m_yprescale; bool m_double_buffer; uint32_t m_style; diff --git a/src/osd/modules/render/bgfx/targetmanager.cpp b/src/osd/modules/render/bgfx/targetmanager.cpp index dfd5b2adbd1..eae09c48008 100644 --- a/src/osd/modules/render/bgfx/targetmanager.cpp +++ b/src/osd/modules/render/bgfx/targetmanager.cpp @@ -9,15 +9,15 @@ // //============================================================ -#include <bgfx/bgfx.h> +#include "targetmanager.h" -#include <vector> +#include "bgfxutil.h" +#include "target.h" #include "modules/lib/osdobj_common.h" -#include "targetmanager.h" +#include <utility> -#include "target.h" const int32_t target_manager::MAX_SCREENS = 100; @@ -29,100 +29,106 @@ target_manager::target_manager(texture_manager& textures) target_manager::~target_manager() { - std::vector<bgfx_target*> to_delete; - for (std::pair<std::string, bgfx_target*> target : m_targets) - { - if (target.second != nullptr) - { - to_delete.push_back(target.second); - } - } - m_targets.clear(); - - for (uint32_t i = 0; i < to_delete.size(); i++) - { - delete to_delete[i]; - } + for (auto iter = m_targets.begin(); iter != m_targets.end(); iter = m_targets.erase(iter)) + m_textures.remove_provider(iter->first); } -bgfx_target* target_manager::create_target(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen) +bgfx_target* target_manager::create_target( + std::string &&name, + bgfx::TextureFormat::Enum format, + uint16_t width, + uint16_t height, + uint16_t xprescale, + uint16_t yprescale, + uint32_t style, + bool double_buffer, + bool filter, + uint16_t scale, + uint32_t screen) { - std::string full_name = name + std::to_string(screen); + const std::string full_name = name + std::to_string(screen); auto iter = m_targets.find(full_name); if (iter != m_targets.end()) - destroy_target(name, screen); + { + iter->second.reset(); + m_textures.remove_provider(full_name); + } - bgfx_target *target = new bgfx_target(name, format, width, height, style, double_buffer, filter, scale, screen); - m_targets[full_name] = target; - m_textures.add_provider(full_name, target); + auto target = std::make_unique<bgfx_target>(std::move(name), format, width, height, xprescale, yprescale, style, double_buffer, filter, scale, screen); + if (iter != m_targets.end()) + iter->second = std::move(target); + else + iter = m_targets.emplace(full_name, std::move(target)).first; + m_textures.add_provider(full_name, *iter->second); - return target; + return iter->second.get(); } -void target_manager::destroy_target(std::string name, uint32_t screen) +void target_manager::destroy_target(const std::string &name, uint32_t screen) { - std::string full_name = (screen < 0) ? name : (name + std::to_string(screen)); - if (m_targets.find(full_name) != m_targets.end()) + const std::string full_name = (screen < 0) ? name : (name + std::to_string(screen)); + const auto found = m_targets.find(full_name); + if (found != m_targets.end()) { - delete m_targets[full_name]; - m_targets.erase(full_name); + m_targets.erase(found); m_textures.remove_provider(full_name); } } bgfx_target* target_manager::create_backbuffer(void *handle, uint16_t width, uint16_t height) { - auto* target = new bgfx_target(handle, width, height); - m_targets["backbuffer"] = target; - return target; + auto target = std::make_unique<bgfx_target>(handle, width, height); + return (m_targets["backbuffer"] = std::move(target)).get(); } -bgfx_target* target_manager::target(uint32_t screen, std::string name) +bgfx_target* target_manager::target(uint32_t screen, const std::string &name) { - std::string full_name = name + std::to_string(screen); - if (m_targets.find(full_name) == m_targets.end()) + const std::string full_name = name + std::to_string(screen); + const auto found = m_targets.find(full_name); + if (found != m_targets.end()) + { + return found->second.get(); + } + else { osd_printf_verbose("Warning: Attempting to retrieve a nonexistent target '%s' for screen %d\n", name, screen); + return nullptr; } - return m_targets[full_name]; } -bool target_manager::update_target_sizes(uint32_t screen, uint16_t width, uint16_t height, uint32_t style) +bool target_manager::update_target_sizes(uint32_t screen, uint16_t width, uint16_t height, uint32_t style, uint16_t user_prescale, uint16_t max_prescale_size) { - if (style == TARGET_STYLE_CUSTOM) return false; + if (style == TARGET_STYLE_CUSTOM) + return false; - std::vector<osd_dim>& sizes = style == TARGET_STYLE_GUEST ? m_guest_dims : m_native_dims; + std::vector<osd_dim> &sizes = (style == TARGET_STYLE_GUEST) ? m_guest_dims : m_native_dims; // Ensure that there's an entry to fill while (sizes.size() <= screen) { - sizes.emplace_back(osd_dim(0, 0)); + sizes.emplace_back(0, 0); } if (width != sizes[screen].width() || height != sizes[screen].height()) { sizes[screen] = osd_dim(width, height); - rebuild_targets(screen, style); + rebuild_targets(screen, style, user_prescale, max_prescale_size); return true; } return false; } -void target_manager::rebuild_targets(uint32_t screen, uint32_t style) +void target_manager::rebuild_targets(uint32_t screen, uint32_t style, uint16_t user_prescale, uint16_t max_prescale_size) { if (style == TARGET_STYLE_CUSTOM) return; std::vector<bgfx_target*> to_resize; - for (std::pair<std::string, bgfx_target*> target_pair : m_targets) + for (const auto &[name, target] : m_targets) { - bgfx_target* target = target_pair.second; - if (target == nullptr || target->style() != style || target->screen_index() != screen) - { - continue; - } - to_resize.push_back(target); + if (target && (target->style() == style) && (target->screen_index() == screen)) + to_resize.push_back(target.get()); } std::vector<osd_dim>& sizes = style == TARGET_STYLE_GUEST ? m_guest_dims : m_native_dims; @@ -135,13 +141,15 @@ void target_manager::rebuild_targets(uint32_t screen, uint32_t style) const uint16_t scale = target->scale(); const uint16_t width(sizes[screen].width()); const uint16_t height(sizes[screen].height()); + uint16_t xprescale = user_prescale; + uint16_t yprescale = user_prescale; + bgfx_util::find_prescale_factor(width, height, max_prescale_size, xprescale, yprescale); - destroy_target(name, screen); - create_target(name, format, width, height, style, double_buffered, filter, scale, screen); + create_target(std::move(name), format, width, height, xprescale, yprescale, style, double_buffered, filter, scale, screen); } } -void target_manager::update_screen_count(uint32_t count) +void target_manager::update_screen_count(uint32_t count, uint16_t user_prescale, uint16_t max_prescale_size) { // Ensure that there's an entry to fill while (count > m_native_dims.size()) @@ -157,26 +165,26 @@ void target_manager::update_screen_count(uint32_t count) { for (uint32_t screen = old_count; screen < m_screen_count; screen++) { - create_target_if_nonexistent(screen, "output", false, false, TARGET_STYLE_NATIVE); + create_output_if_nonexistent(screen, user_prescale, max_prescale_size); } } } } -void target_manager::create_target_if_nonexistent(uint32_t screen, std::string name, bool double_buffered, bool filter, uint32_t style) +void target_manager::create_output_if_nonexistent(uint32_t screen, uint16_t user_prescale, uint16_t max_prescale_size) { - if (style == TARGET_STYLE_CUSTOM) return; - - if (m_targets.find(name + std::to_string(screen)) != m_targets.end()) + if (m_targets.find("output" + std::to_string(screen)) != m_targets.end()) { return; } - std::vector<osd_dim>& sizes = style == TARGET_STYLE_GUEST ? m_guest_dims : m_native_dims; - uint16_t width(sizes[screen].width()); - uint16_t height(sizes[screen].height()); + uint16_t width(m_native_dims[screen].width()); + uint16_t height(m_native_dims[screen].height()); + uint16_t xprescale = user_prescale; + uint16_t yprescale = user_prescale; + bgfx_util::find_prescale_factor(width, height, max_prescale_size, xprescale, yprescale); - create_target(name, bgfx::TextureFormat::BGRA8, width, height, style, double_buffered, filter, 1, screen); + create_target("output", bgfx::TextureFormat::BGRA8, width, height, xprescale, yprescale, TARGET_STYLE_NATIVE, false, false, 1, screen); } uint16_t target_manager::width(uint32_t style, uint32_t screen) diff --git a/src/osd/modules/render/bgfx/targetmanager.h b/src/osd/modules/render/bgfx/targetmanager.h index fa44e2c1bd6..38bba4c5db5 100644 --- a/src/osd/modules/render/bgfx/targetmanager.h +++ b/src/osd/modules/render/bgfx/targetmanager.h @@ -9,44 +9,49 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_TARGETMANAGER_H +#define MAME_RENDER_BGFX_TARGETMANAGER_H + #pragma once -#ifndef __DRAWBGFX_TARGET_MANAGER__ -#define __DRAWBGFX_TARGET_MANAGER__ +#include "texturemanager.h" -#include <map> -#include <string> +#include "modules/osdhelper.h" #include <bgfx/bgfx.h> -#include "modules/osdhelper.h" +#include <map> +#include <memory> +#include <string> +#include <vector> -#include "texturemanager.h" class bgfx_target; -class target_manager { +class target_manager +{ public: target_manager(texture_manager& textures); ~target_manager(); - bgfx_target* create_target(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen); - void destroy_target(std::string name, uint32_t screen = -1); + bgfx_target* create_target(std::string &&name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint16_t xprescale, uint16_t yprescale, + uint32_t style, bool double_buffer, bool filter, uint16_t scale, uint32_t screen); + void destroy_target(const std::string &name, uint32_t screen = -1); bgfx_target* create_backbuffer(void *handle, uint16_t width, uint16_t height); - bool update_target_sizes(uint32_t screen, uint16_t width, uint16_t height, uint32_t style); - void update_screen_count(uint32_t count); + bool update_target_sizes(uint32_t screen, uint16_t width, uint16_t height, uint32_t style, uint16_t user_prescale, uint16_t max_prescale_size); + void update_screen_count(uint32_t count, uint16_t user_prescale, uint16_t max_prescale_size); // Getters - bgfx_target* target(uint32_t screen, std::string name); + bgfx_target* target(uint32_t screen, const std::string &name); uint16_t width(uint32_t style, uint32_t screen); uint16_t height(uint32_t style, uint32_t screen); private: - void rebuild_targets(uint32_t screen, uint32_t style); - void create_target_if_nonexistent(uint32_t screen, std::string name, bool double_buffered, bool filter, uint32_t style); + void rebuild_targets(uint32_t screen, uint32_t style, uint16_t user_prescale, uint16_t max_prescale_size); + void create_output_if_nonexistent(uint32_t screen, uint16_t user_prescale, uint16_t max_prescale_size); - std::map<std::string, bgfx_target*> m_targets; + std::map<std::string, std::unique_ptr<bgfx_target> > m_targets; texture_manager& m_textures; std::vector<osd_dim> m_guest_dims; @@ -56,4 +61,4 @@ private: static const int32_t MAX_SCREENS; }; -#endif // __DRAWBGFX_TARGET_MANAGER__ +#endif // MAME_RENDER_BGFX_TARGETMANAGER_H diff --git a/src/osd/modules/render/bgfx/targetreader.cpp b/src/osd/modules/render/bgfx/targetreader.cpp index e66a147d7ba..12386428822 100644 --- a/src/osd/modules/render/bgfx/targetreader.cpp +++ b/src/osd/modules/render/bgfx/targetreader.cpp @@ -8,11 +8,12 @@ #include "targetreader.h" -#include <modules/lib/osdobj_common.h> - +#include "bgfxutil.h" #include "chainmanager.h" #include "target.h" +#include "modules/lib/osdobj_common.h" + #include <cmath> const target_reader::string_to_enum target_reader::STYLE_NAMES[target_reader::STYLE_COUNT] = { @@ -21,7 +22,13 @@ const target_reader::string_to_enum target_reader::STYLE_NAMES[target_reader::ST { "custom", TARGET_STYLE_CUSTOM } }; -bgfx_target* target_reader::read_from_value(const Value& value, std::string prefix, chain_manager& chains, uint32_t screen_index) +bgfx_target* target_reader::read_from_value( + const Value& value, + const std::string &prefix, + chain_manager& chains, + uint32_t screen_index, + uint16_t user_prescale, + uint16_t max_prescale_size) { if (!validate_parameters(value, prefix)) { @@ -37,40 +44,61 @@ bgfx_target* target_reader::read_from_value(const Value& value, std::string pref { scale = int(floor(value["scale"].GetDouble() + 0.5)); } + bool use_user_prescale = get_bool(value, "user_prescale", false); uint16_t width = 0; uint16_t height = 0; + uint16_t xprescale = 1; + uint16_t yprescale = 1; switch (mode) { case TARGET_STYLE_GUEST: + { width = chains.targets().width(TARGET_STYLE_GUEST, screen_index); height = chains.targets().height(TARGET_STYLE_GUEST, screen_index); + + if (use_user_prescale) + { + xprescale = user_prescale; + yprescale = user_prescale; + bgfx_util::find_prescale_factor(width, height, max_prescale_size, xprescale, yprescale); + } break; + } case TARGET_STYLE_NATIVE: width = chains.targets().width(TARGET_STYLE_NATIVE, screen_index); height = chains.targets().height(TARGET_STYLE_NATIVE, screen_index); + + if (use_user_prescale) + { + xprescale = user_prescale; + yprescale = user_prescale; + bgfx_util::find_prescale_factor(width, height, max_prescale_size, xprescale, yprescale); + } break; case TARGET_STYLE_CUSTOM: - if (!READER_CHECK(value.HasMember("width"), (prefix + "Target '" + target_name + "': Must have numeric value 'width'\n").c_str())) return nullptr; - if (!READER_CHECK(value["width"].IsNumber(), (prefix + "Target '" + target_name + "': Value 'width' must be a number\n").c_str())) return nullptr; - if (!READER_CHECK(value.HasMember("height"), (prefix + "Target '" + target_name + "': Must have numeric value 'height'\n").c_str())) return nullptr; - if (!READER_CHECK(value["height"].IsNumber(), (prefix + "Target '" + target_name + "': Value 'height' must be a number\n").c_str())) return nullptr; + READER_WARN(!value.HasMember("user_prescale"), "%sTarget '%s': user_prescale parameter is not used for custom-type render targets.\n", prefix, target_name); + if (!READER_CHECK(value.HasMember("width"), "%sTarget '%s': Must have numeric value 'width'\n", prefix, target_name)) return nullptr; + if (!READER_CHECK(value["width"].IsNumber(), "%sTarget '%s': Value 'width' must be a number\n", prefix, target_name)) return nullptr; + if (!READER_CHECK(value.HasMember("height"), "%sTarget '%s': Must have numeric value 'height'\n", prefix, target_name)) return nullptr; + if (!READER_CHECK(value["height"].IsNumber(), "%sTarget '%s': Value 'height' must be a number\n", prefix, target_name)) return nullptr; width = uint16_t(value["width"].GetDouble()); height = uint16_t(value["height"].GetDouble()); break; } - return chains.targets().create_target(target_name, bgfx::TextureFormat::BGRA8, width, height, mode, double_buffer, bilinear, scale, screen_index); + return chains.targets().create_target(std::move(target_name), bgfx::TextureFormat::BGRA8, width, height, xprescale, yprescale, mode, double_buffer, bilinear, scale, screen_index); } -bool target_reader::validate_parameters(const Value& value, std::string prefix) +bool target_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'name'\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("mode"), (prefix + "Must have string enum 'mode'\n").c_str())) return false; - if (!READER_CHECK(value["mode"].IsString(), (prefix + "Value 'mode' must be a string (what screens does this apply to?)\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("bilinear") || value["bilinear"].IsBool(), (prefix + "Value 'bilinear' must be a boolean\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("doublebuffer") || value["doublebuffer"].IsBool(), (prefix + "Value 'doublebuffer' must be a boolean\n").c_str())) return false; - if (!READER_CHECK(!value.HasMember("scale") || value["scale"].IsNumber(), (prefix + "Value 'scale' must be a numeric value\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("name"), "%sMust have string value 'name'\n", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%sValue 'name' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("mode"), "%sMust have string enum 'mode'\n", prefix)) return false; + if (!READER_CHECK(value["mode"].IsString(), "%sValue 'mode' must be a string (what screens does this apply to?)\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("bilinear") || value["bilinear"].IsBool(), "%sValue 'bilinear' must be a boolean\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("doublebuffer") || value["doublebuffer"].IsBool(), "%sValue 'doublebuffer' must be a boolean\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("user_prescale") || value["user_prescale"].IsBool(), "%sValue 'user_prescale' must be a boolean\n", prefix)) return false; + if (!READER_CHECK(!value.HasMember("scale") || value["scale"].IsNumber(), "%sValue 'scale' must be a numeric value\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/targetreader.h b/src/osd/modules/render/bgfx/targetreader.h index 7edf5e456fa..2db81311359 100644 --- a/src/osd/modules/render/bgfx/targetreader.h +++ b/src/osd/modules/render/bgfx/targetreader.h @@ -6,28 +6,28 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_TARGETREADER_H +#define MAME_RENDER_BGFX_TARGETREADER_H + #pragma once -#ifndef __DRAWBGFX_TARGET_READER__ -#define __DRAWBGFX_TARGET_READER__ +#include "statereader.h" #include <string> -#include "statereader.h" - class bgfx_target; class chain_manager; class target_reader : public state_reader { public: - static bgfx_target* read_from_value(const Value& value, std::string prefix, chain_manager& chains, uint32_t screen_index); + static bgfx_target* read_from_value(const Value& value, const std::string &prefix, chain_manager& chains, uint32_t screen_index, uint16_t user_prescale, uint16_t max_prescale_size); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); static const int STYLE_COUNT = 3; static const string_to_enum STYLE_NAMES[STYLE_COUNT]; }; -#endif // __DRAWBGFX_TARGET_READER__ +#endif // MAME_RENDER_BGFX_TARGETREADER_H diff --git a/src/osd/modules/render/bgfx/texture.cpp b/src/osd/modules/render/bgfx/texture.cpp index a504c625415..2ffa97c0b6f 100644 --- a/src/osd/modules/render/bgfx/texture.cpp +++ b/src/osd/modules/render/bgfx/texture.cpp @@ -10,10 +10,11 @@ #include "texture.h" -bgfx_texture::bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint32_t flags, void* data) +bgfx_texture::bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t width_margin, uint16_t height, uint32_t flags, void* data) : m_name(name) , m_format(format) , m_width(width) + , m_width_margin(width_margin) , m_height(height) , m_rowpixels(width) , m_width_div_factor(1) @@ -35,10 +36,11 @@ bgfx_texture::bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, u } } -bgfx_texture::bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, const bgfx::Memory* data, uint32_t flags, uint16_t pitch, uint16_t rowpixels, int width_div_factor, int width_mul_factor) +bgfx_texture::bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t width_margin, uint16_t height, const bgfx::Memory* data, uint32_t flags, uint16_t pitch, uint16_t rowpixels, int width_div_factor, int width_mul_factor) : m_name(name) , m_format(format) , m_width(width) + , m_width_margin(width_margin) , m_height(height) , m_rowpixels(rowpixels ? rowpixels : width) , m_width_div_factor(width_div_factor) @@ -56,7 +58,8 @@ bgfx_texture::~bgfx_texture() bgfx::destroy(m_texture); } -void bgfx_texture::update(const bgfx::Memory *data, uint16_t pitch) +void bgfx_texture::update(const bgfx::Memory *data, uint16_t pitch, uint16_t width_margin) { + m_width_margin = width_margin; bgfx::updateTexture2D(m_texture, 0, 0, 0, 0, (m_rowpixels * m_width_mul_factor) / m_width_div_factor, m_height, data, pitch); } diff --git a/src/osd/modules/render/bgfx/texture.h b/src/osd/modules/render/bgfx/texture.h index 75fedcd3129..ced0604dfcf 100644 --- a/src/osd/modules/render/bgfx/texture.h +++ b/src/osd/modules/render/bgfx/texture.h @@ -20,8 +20,8 @@ class bgfx_texture : public bgfx_texture_handle_provider { public: - bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, uint32_t flags, void* data); - bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t height, const bgfx::Memory* data, uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP, uint16_t pitch = UINT16_MAX, uint16_t rowpixels = 0, int width_div_factor = 1, int width_mul_factor = 1); + bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t width_margin, uint16_t height, uint32_t flags, void* data); + bgfx_texture(std::string name, bgfx::TextureFormat::Enum format, uint16_t width, uint16_t width_margin, uint16_t height, const bgfx::Memory* data, uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP, uint16_t pitch = UINT16_MAX, uint16_t rowpixels = 0, int width_div_factor = 1, int width_mul_factor = 1); virtual ~bgfx_texture(); // Getters @@ -32,17 +32,19 @@ public: virtual bgfx::TextureHandle texture() const override { return m_texture; } virtual bool is_target() const override { return false; } virtual uint16_t width() const override { return m_width; } + virtual uint16_t width_margin() const override { return m_width_margin; } virtual uint16_t height() const override { return m_height; } virtual uint16_t rowpixels() const override { return m_rowpixels; } virtual int width_div_factor() const override { return m_width_div_factor; } virtual int width_mul_factor() const override { return m_width_mul_factor; } - void update(const bgfx::Memory *data, uint16_t pitch = UINT16_MAX); + void update(const bgfx::Memory *data, uint16_t pitch = UINT16_MAX, uint16_t width_margin = 0); protected: std::string m_name; bgfx::TextureFormat::Enum m_format; uint16_t m_width; + uint16_t m_width_margin; uint16_t m_height; uint16_t m_rowpixels; int m_width_div_factor; diff --git a/src/osd/modules/render/bgfx/texturehandleprovider.h b/src/osd/modules/render/bgfx/texturehandleprovider.h index 14fb2fe892c..35e4e12dc43 100644 --- a/src/osd/modules/render/bgfx/texturehandleprovider.h +++ b/src/osd/modules/render/bgfx/texturehandleprovider.h @@ -7,26 +7,27 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_TEXTUREHANDLEPROVIDER_H +#define MAME_RENDER_BGFX_TEXTUREHANDLEPROVIDER_H -#ifndef __DRAWBGFX_TEXTURE_HANDLE_PROVIDER__ -#define __DRAWBGFX_TEXTURE_HANDLE_PROVIDER__ +#pragma once #include <bgfx/bgfx.h> class bgfx_texture_handle_provider { public: - virtual ~bgfx_texture_handle_provider() { } + virtual ~bgfx_texture_handle_provider() = default; // Getters virtual bgfx::TextureHandle texture() const = 0; virtual bool is_target() const = 0; virtual uint16_t width() const = 0; + virtual uint16_t width_margin() const = 0; virtual uint16_t height() const = 0; virtual uint16_t rowpixels() const = 0; virtual int width_div_factor() const = 0; virtual int width_mul_factor() const = 0; }; -#endif // __DRAWBGFX_TEXTURE_HANDLE_PROVIDER__ +#endif // MAME_RENDER_BGFX_TEXTUREHANDLEPROVIDER_H diff --git a/src/osd/modules/render/bgfx/texturemanager.cpp b/src/osd/modules/render/bgfx/texturemanager.cpp index 82225f87eb0..b9acd5748e8 100644 --- a/src/osd/modules/render/bgfx/texturemanager.cpp +++ b/src/osd/modules/render/bgfx/texturemanager.cpp @@ -11,23 +11,24 @@ #include "texturemanager.h" -#include "texture.h" #include "bgfxutil.h" +#include "texture.h" + +#include "modules/render/copyutil.h" +#include "osdcore.h" + #include "emucore.h" #include "fileio.h" #include "rendutil.h" -#include "osdcore.h" -#include "modules/render/copyutil.h" + + +texture_manager::texture_manager() +{ + // out-of-line so header works with forward declarations +} texture_manager::~texture_manager() { - for (std::pair<std::string, bgfx_texture_handle_provider*> texture : m_textures) - { - if (texture.second != nullptr && !(texture.second)->is_target()) - { - delete texture.second; - } - } m_textures.clear(); for (std::pair<uint64_t, sequenced_handle> mame_texture : m_mame_textures) @@ -37,27 +38,45 @@ texture_manager::~texture_manager() m_mame_textures.clear(); } -void texture_manager::add_provider(std::string name, bgfx_texture_handle_provider* provider) +void texture_manager::add_provider(const std::string &name, std::unique_ptr<bgfx_texture_handle_provider> &&provider) { - auto iter = m_textures.find(name); + const auto iter = m_textures.find(name); if (iter != m_textures.end()) - { - iter->second = provider; - } + iter->second = std::make_pair(provider.get(), std::move(provider)); else - { - m_textures[name] = provider; - } + m_textures.emplace(name, std::make_pair(provider.get(), std::move(provider))); } -bgfx_texture* texture_manager::create_texture(std::string name, bgfx::TextureFormat::Enum format, uint32_t width, uint32_t height, void* data, uint32_t flags) +void texture_manager::add_provider(const std::string &name, bgfx_texture_handle_provider &provider) { - auto* texture = new bgfx_texture(name, format, width, height, flags, data); - m_textures[name] = texture; - return texture; + const auto iter = m_textures.find(name); + if (iter != m_textures.end()) + iter->second = std::make_pair(&provider, nullptr); + else + m_textures.emplace(name, std::make_pair(&provider, nullptr)); +} + +bgfx_texture* texture_manager::create_texture( + const std::string &name, + bgfx::TextureFormat::Enum format, + uint32_t width, + uint32_t width_margin, + uint32_t height, + void* data, + uint32_t flags) +{ + auto texture = std::make_unique<bgfx_texture>(name, format, width, width_margin, height, flags, data); + bgfx_texture &result = *texture; + m_textures[name] = std::make_pair(texture.get(), std::move(texture)); + return &result; } -bgfx_texture* texture_manager::create_png_texture(std::string path, std::string file_name, std::string texture_name, uint32_t flags, uint32_t screen) +bgfx_texture* texture_manager::create_png_texture( + const std::string &path, + const std::string &file_name, + std::string texture_name, + uint32_t flags, + uint32_t screen) { bitmap_argb32 bitmap; emu_file file(path, OPEN_FLAG_READ); @@ -69,40 +88,45 @@ bgfx_texture* texture_manager::create_png_texture(std::string path, std::string if (bitmap.width() == 0 || bitmap.height() == 0) { - osd_printf_error("Unable to load PNG '%s' from path '%s'\n", file_name.c_str(), path.c_str()); + osd_printf_error("Unable to load PNG '%s' from path '%s'\n", file_name, path); return nullptr; } - auto *data = new uint8_t[bitmap.width() * bitmap.height() * 4]; - auto *data32 = reinterpret_cast<uint32_t *>(data); - const uint32_t width = bitmap.width(); const uint32_t height = bitmap.height(); + auto data32 = std::make_unique<uint32_t []>(width * height); + const uint32_t rowpixels = bitmap.rowpixels(); auto* base = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(0)); for (int y = 0; y < height; y++) { - copy_util::copyline_argb32_to_bgra(data32 + y * width, base + y * rowpixels, width, nullptr); + copy_util::copyline_argb32_to_bgra(&data32[y * width], base + y * rowpixels, width, nullptr); } if (screen >= 0) { texture_name += std::to_string(screen); } - bgfx_texture* texture = create_texture(texture_name, bgfx::TextureFormat::BGRA8, width, height, data, flags); - - delete [] data; - - return texture; + return create_texture(texture_name, bgfx::TextureFormat::BGRA8, width, 0, height, data32.get(), flags); } -bgfx::TextureHandle texture_manager::create_or_update_mame_texture(uint32_t format, int width, int height - , int rowpixels, const rgb_t *palette, void *base, uint32_t seqid, uint32_t flags, uint64_t key, uint64_t old_key) +bgfx::TextureHandle texture_manager::create_or_update_mame_texture( + uint32_t format, + int width, + int width_margin, + int height, + int rowpixels, + const rgb_t *palette, + void *base, + uint32_t seqid, + uint32_t flags, + uint64_t key, + uint64_t old_key) { bgfx::TextureHandle handle = BGFX_INVALID_HANDLE; if (old_key != ~0ULL) { - auto iter = m_mame_textures.find(old_key); + const auto iter = m_mame_textures.find(old_key); if (iter != m_mame_textures.end()) { handle = iter->second.handle; @@ -126,8 +150,8 @@ bgfx::TextureHandle texture_manager::create_or_update_mame_texture(uint32_t form uint16_t pitch = width; int width_div_factor = 1; int width_mul_factor = 1; - const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, format, rowpixels, height, palette, base, pitch, width_div_factor, width_mul_factor); - bgfx::updateTexture2D(handle, 0, 0, 0, 0, (uint16_t)((rowpixels * width_mul_factor) / width_div_factor), (uint16_t)height, mem, pitch); + const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, format, rowpixels, width_margin, height, palette, base, pitch, width_div_factor, width_mul_factor); + bgfx::updateTexture2D(handle, 0, 0, 0, 0, uint16_t((rowpixels * width_mul_factor) / width_div_factor), uint16_t(height), mem, pitch); return handle; } } @@ -156,8 +180,8 @@ bgfx::TextureHandle texture_manager::create_or_update_mame_texture(uint32_t form uint16_t pitch = width; int width_div_factor = 1; int width_mul_factor = 1; - const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, format, rowpixels, height, palette, base, pitch, width_div_factor, width_mul_factor); - bgfx::updateTexture2D(handle, 0, 0, 0, 0, (uint16_t)((rowpixels * width_mul_factor) / width_div_factor), (uint16_t)height, mem, pitch); + const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, format, rowpixels, width_margin, height, palette, base, pitch, width_div_factor, width_mul_factor); + bgfx::updateTexture2D(handle, 0, 0, 0, 0, uint16_t((rowpixels * width_mul_factor) / width_div_factor), uint16_t(height), mem, pitch); return handle; } } @@ -169,47 +193,36 @@ bgfx::TextureHandle texture_manager::create_or_update_mame_texture(uint32_t form uint16_t pitch = width; int width_div_factor = 1; int width_mul_factor = 1; - const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, format, rowpixels, height, palette, base, pitch, width_div_factor, width_mul_factor); - const uint16_t adjusted_width = (uint16_t)((rowpixels * width_mul_factor) / width_div_factor); + const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, format, rowpixels, width_margin, height, palette, base, pitch, width_div_factor, width_mul_factor); + const uint16_t adjusted_width = uint16_t((rowpixels * width_mul_factor) / width_div_factor); handle = bgfx::createTexture2D(adjusted_width, height, false, 1, dst_format, flags, nullptr); - bgfx::updateTexture2D(handle, 0, 0, 0, 0, adjusted_width, (uint16_t)height, mem, pitch); + bgfx::updateTexture2D(handle, 0, 0, 0, 0, adjusted_width, uint16_t(height), mem, pitch); m_mame_textures[key] = { handle, seqid, width, height }; return handle; } -bgfx::TextureHandle texture_manager::handle(std::string name) +bgfx::TextureHandle texture_manager::handle(const std::string &name) { bgfx::TextureHandle handle = BGFX_INVALID_HANDLE; - auto iter = m_textures.find(name); + const auto iter = m_textures.find(name); if (iter != m_textures.end()) - { - handle = (iter->second)->texture(); - } + handle = iter->second.first->texture(); assert(handle.idx != bgfx::kInvalidHandle); return handle; } -bgfx_texture_handle_provider* texture_manager::provider(std::string name) +bgfx_texture_handle_provider* texture_manager::provider(const std::string &name) { - auto iter = m_textures.find(name); + const auto iter = m_textures.find(name); if (iter != m_textures.end()) - { - return iter->second; - } - - return nullptr; + return iter->second.first; + else + return nullptr; } -void texture_manager::remove_provider(std::string name, bool delete_provider) +void texture_manager::remove_provider(const std::string &name) { - if (provider(name) != nullptr) - { - if (delete_provider) - { - delete m_textures[name]; - } - m_textures.erase(name); - } + m_textures.erase(name); } diff --git a/src/osd/modules/render/bgfx/texturemanager.h b/src/osd/modules/render/bgfx/texturemanager.h index db71f07c0fa..8ed5d410f75 100644 --- a/src/osd/modules/render/bgfx/texturemanager.h +++ b/src/osd/modules/render/bgfx/texturemanager.h @@ -9,40 +9,45 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_TEXTUREMANAGER_H +#define MAME_RENDER_BGFX_TEXTUREMANAGER_H -#ifndef DRAWBGFX_TEXTURE_MANAGER -#define DRAWBGFX_TEXTURE_MANAGER +#pragma once #include "bitmap.h" #include <cstdint> #include <map> +#include <memory> #include <string> +#include <utility> #include <bgfx/bgfx.h> + class bgfx_texture_handle_provider; class bgfx_texture; -class texture_manager { +class texture_manager +{ public: - texture_manager() { } + texture_manager(); ~texture_manager(); - bgfx_texture* create_texture(std::string name, bgfx::TextureFormat::Enum format, uint32_t width, uint32_t height, void* data = nullptr, uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP); - bgfx_texture* create_png_texture(std::string path, std::string file_name, std::string texture_name, uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP, uint32_t screen = -1); - void add_provider(std::string name, bgfx_texture_handle_provider* texture); - void remove_provider(std::string name, bool delete_provider = false); - bgfx::TextureHandle create_or_update_mame_texture(uint32_t format, int width, int height - , int rowpixels, const rgb_t *palette, void *base, uint32_t seqid, uint32_t flags, uint64_t key, uint64_t old_key); + bgfx_texture* create_texture(const std::string &name, bgfx::TextureFormat::Enum format, uint32_t width, uint32_t width_margin, uint32_t height, void* data = nullptr, uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP); + bgfx_texture* create_png_texture(const std::string &path, const std::string &file_name, std::string texture_name, uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP, uint32_t screen = -1); + void add_provider(const std::string &name, std::unique_ptr<bgfx_texture_handle_provider> &&texture); + void add_provider(const std::string &name, bgfx_texture_handle_provider &texture); + void remove_provider(const std::string &name); + bgfx::TextureHandle create_or_update_mame_texture(uint32_t format, int width, int width_margin, int height, + int rowpixels, const rgb_t *palette, void *base, uint32_t seqid, uint32_t flags, uint64_t key, uint64_t old_key); // Getters - bgfx::TextureHandle handle(std::string name); - bgfx_texture_handle_provider* provider(std::string name); + bgfx::TextureHandle handle(const std::string &name); + bgfx_texture_handle_provider* provider(const std::string &name); private: - bgfx_texture* create_texture(std::string name); + bgfx_texture* create_texture(const std::string &name); struct sequenced_handle { @@ -52,8 +57,8 @@ private: int height; }; - std::map<std::string, bgfx_texture_handle_provider*> m_textures; + std::map<std::string, std::pair<bgfx_texture_handle_provider *, std::unique_ptr<bgfx_texture_handle_provider> > > m_textures; std::map<uint64_t, sequenced_handle> m_mame_textures; }; -#endif // DRAWBGFX_TEXTURE_MANAGER +#endif // MAME_RENDER_BGFX_TEXTUREMANAGER_H diff --git a/src/osd/modules/render/bgfx/timeparameter.cpp b/src/osd/modules/render/bgfx/timeparameter.cpp index cbcd56c8e6e..bc01bb33b61 100644 --- a/src/osd/modules/render/bgfx/timeparameter.cpp +++ b/src/osd/modules/render/bgfx/timeparameter.cpp @@ -8,8 +8,10 @@ #include "timeparameter.h" -bgfx_time_parameter::bgfx_time_parameter(std::string name, parameter_type type, double limit) - : bgfx_parameter(name, type) +#include <utility> + +bgfx_time_parameter::bgfx_time_parameter(std::string &&name, parameter_type type, double limit) + : bgfx_parameter(std::move(name), type) , m_current_time(0) , m_limit(limit) { diff --git a/src/osd/modules/render/bgfx/timeparameter.h b/src/osd/modules/render/bgfx/timeparameter.h index ad01f8f7e60..ff7005041ff 100644 --- a/src/osd/modules/render/bgfx/timeparameter.h +++ b/src/osd/modules/render/bgfx/timeparameter.h @@ -6,22 +6,19 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_TIMEPARAMETER_H +#define MAME_RENDER_BGFX_TIMEPARAMETER_H -#ifndef __DRAWBGFX_TIME_PARAMETER__ -#define __DRAWBGFX_TIME_PARAMETER__ +#pragma once -#include <bgfx/bgfx.h> +#include "parameter.h" #include <string> -#include "parameter.h" - class bgfx_time_parameter : public bgfx_parameter { public: - bgfx_time_parameter(std::string name, parameter_type type, double limit); - virtual ~bgfx_time_parameter() { } + bgfx_time_parameter(std::string &&name, parameter_type type, double limit); virtual float value() override; virtual void tick(double delta) override; @@ -31,4 +28,4 @@ private: double m_limit; }; -#endif // __DRAWBGFX_TIME_PARAMETER__ +#endif // MAME_RENDER_BGFX_TIMEPARAMETER_H diff --git a/src/osd/modules/render/bgfx/uniform.cpp b/src/osd/modules/render/bgfx/uniform.cpp index 36f68b7a9c7..238a6a4315d 100644 --- a/src/osd/modules/render/bgfx/uniform.cpp +++ b/src/osd/modules/render/bgfx/uniform.cpp @@ -7,10 +7,12 @@ //============================================================ #include "uniform.h" + #include <cstring> +#include <utility> -bgfx_uniform::bgfx_uniform(std::string name, bgfx::UniformType::Enum type) - : m_name(name) +bgfx_uniform::bgfx_uniform(std::string &&name, bgfx::UniformType::Enum type) + : m_name(std::move(name)) , m_type(type) { m_handle = BGFX_INVALID_HANDLE; diff --git a/src/osd/modules/render/bgfx/uniform.h b/src/osd/modules/render/bgfx/uniform.h index f7873f400a9..6a9999ca19f 100644 --- a/src/osd/modules/render/bgfx/uniform.h +++ b/src/osd/modules/render/bgfx/uniform.h @@ -6,10 +6,10 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_UNIFORM_H +#define MAME_RENDER_BGFX_UNIFORM_H -#ifndef __DRAWBGFX_UNIFORM__ -#define __DRAWBGFX_UNIFORM__ +#pragma once #include <bgfx/bgfx.h> @@ -18,7 +18,7 @@ class bgfx_uniform { public: - bgfx_uniform(std::string name, bgfx::UniformType::Enum type); + bgfx_uniform(std::string &&name, bgfx::UniformType::Enum type); virtual ~bgfx_uniform(); virtual void upload(); @@ -26,7 +26,7 @@ public: void create(); // Getters - std::string name() { return m_name; } + const std::string &name() { return m_name; } bgfx::UniformType::Enum type() const { return m_type; } bgfx::UniformHandle handle() const { return m_handle; } @@ -47,4 +47,4 @@ protected: size_t m_data_size; }; -#endif // __DRAWBGFX_UNIFORM__ +#endif // MAME_RENDER_BGFX_UNIFORM_H diff --git a/src/osd/modules/render/bgfx/uniformreader.cpp b/src/osd/modules/render/bgfx/uniformreader.cpp index f2bf1114ec6..d1eab8098c7 100644 --- a/src/osd/modules/render/bgfx/uniformreader.cpp +++ b/src/osd/modules/render/bgfx/uniformreader.cpp @@ -10,6 +10,9 @@ #include "uniform.h" +#include <cstdlib> + + const uniform_reader::string_to_enum uniform_reader::TYPE_NAMES[uniform_reader::TYPE_COUNT] = { { "int", bgfx::UniformType::Sampler }, { "vec4", bgfx::UniformType::Vec4 }, @@ -17,7 +20,7 @@ const uniform_reader::string_to_enum uniform_reader::TYPE_NAMES[uniform_reader:: { "mat4", bgfx::UniformType::Mat4 } }; -bgfx_uniform* uniform_reader::read_from_value(const Value& value, std::string prefix) +std::unique_ptr<bgfx_uniform> uniform_reader::read_from_value(const Value& value, const std::string &prefix) { if (!validate_parameters(value, prefix)) { @@ -31,33 +34,28 @@ bgfx_uniform* uniform_reader::read_from_value(const Value& value, std::string pr const Value& value_array = value["values"]; const size_t array_size = value_array.Size() * sizeof(float); - const size_t alloc_size = (type_size > array_size) ? type_size : array_size; - auto* data = reinterpret_cast<float*>(new char[alloc_size]); + auto* data = reinterpret_cast<float*>(std::malloc(std::max(type_size, array_size))); unsigned int index = 0; for (; index < type_size / 4 && index < value_array.Size(); index++) - { - data[index] = (float)value_array[index].GetDouble(); - } + data[index] = float(value_array[index].GetDouble()); for (; index < type_size / 4; index++) - { data[index] = 0.0f; - } - auto* uniform = new bgfx_uniform(name, type); - uniform->set((void*)data, type_size); - delete [] data; + auto uniform = std::make_unique<bgfx_uniform>(name, type); + uniform->set(data, type_size); + std::free(data); return uniform; } -bool uniform_reader::validate_parameters(const Value& value, std::string prefix) +bool uniform_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("name"), (prefix + "Must have string value 'name' (what is this uniform called in the shader code?)\n").c_str())) return false; - if (!READER_CHECK(value["name"].IsString(), (prefix + "Value 'name' must be a string\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("type"), (prefix + "Must have string value 'type' [int, vec4, mat3, mat4]\n").c_str())) return false; - if (!READER_CHECK(value.HasMember("values"), (prefix + "Must have array value 'values' (what are the uniform's default values?)\n").c_str())) return false; - if (!READER_CHECK(value["values"].IsArray(), (prefix + "Value 'values' must be an array\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("name"), "%sMust have string value 'name' (what is this uniform called in the shader code?)\n", prefix)) return false; + if (!READER_CHECK(value["name"].IsString(), "%sValue 'name' must be a string\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("type"), "%sMust have string value 'type' [int, vec4, mat3, mat4]\n", prefix)) return false; + if (!READER_CHECK(value.HasMember("values"), "%sMust have array value 'values' (what are the uniform's default values?)\n", prefix)) return false; + if (!READER_CHECK(value["values"].IsArray(), "%sValue 'values' must be an array\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/uniformreader.h b/src/osd/modules/render/bgfx/uniformreader.h index ce1b1e9ccbe..ebb3a485d63 100644 --- a/src/osd/modules/render/bgfx/uniformreader.h +++ b/src/osd/modules/render/bgfx/uniformreader.h @@ -6,27 +6,29 @@ // //============================================================ +#ifndef MAME_RENDER_BGFX_UNIFORMREADER_H +#define MAME_RENDER_BGFX_UNIFORMREADER_H + #pragma once -#ifndef __DRAWBGFX_UNIFORM_READER__ -#define __DRAWBGFX_UNIFORM_READER__ +#include "statereader.h" -#include <bgfx/bgfx.h> +#include <memory> +#include <string> -#include "statereader.h" class bgfx_uniform; class uniform_reader : public state_reader { public: - static bgfx_uniform* read_from_value(const Value& value, std::string prefix); + static std::unique_ptr<bgfx_uniform> read_from_value(const Value& value, const std::string &prefix); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); static const int TYPE_COUNT = 4; static const string_to_enum TYPE_NAMES[TYPE_COUNT]; }; -#endif // __DRAWBGFX_UNIFORM_READER__ +#endif // MAME_RENDER_BGFX_UNIFORMREADER_H diff --git a/src/osd/modules/render/bgfx/valueuniform.cpp b/src/osd/modules/render/bgfx/valueuniform.cpp index 8a018ac492a..e36ee3cbca8 100644 --- a/src/osd/modules/render/bgfx/valueuniform.cpp +++ b/src/osd/modules/render/bgfx/valueuniform.cpp @@ -10,13 +10,17 @@ //============================================================ #include "valueuniform.h" -#include <cstring> -bgfx_value_uniform::bgfx_value_uniform(bgfx_uniform* uniform, float* values, const int count) +#include <algorithm> +#include <cassert> + + +bgfx_value_uniform::bgfx_value_uniform(bgfx_uniform* uniform, const float* values, const int count) : bgfx_entry_uniform(uniform) , m_count(count) { - memcpy(m_values, values, sizeof(float) * count); + assert(count <= std::size(m_values)); + std::copy_n(values, count, m_values); } void bgfx_value_uniform::bind() diff --git a/src/osd/modules/render/bgfx/valueuniform.h b/src/osd/modules/render/bgfx/valueuniform.h index 40d207aa0bb..aa8139650a3 100644 --- a/src/osd/modules/render/bgfx/valueuniform.h +++ b/src/osd/modules/render/bgfx/valueuniform.h @@ -9,17 +9,17 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_VALUEUNIFORM_H +#define MAME_RENDER_BGFX_VALUEUNIFORM_H -#ifndef __DRAWBGFX_VALUE_UNIFORM__ -#define __DRAWBGFX_VALUE_UNIFORM__ +#pragma once #include "entryuniform.h" class bgfx_value_uniform : public bgfx_entry_uniform { public: - bgfx_value_uniform(bgfx_uniform* uniform, float* values, const int count); + bgfx_value_uniform(bgfx_uniform* uniform, const float* values, const int count); virtual void bind() override; @@ -28,4 +28,4 @@ private: const int m_count; }; -#endif // __DRAWBGFX_VALUE_UNIFORM__ +#endif // MAME_RENDER_BGFX_VALUEUNIFORM_H diff --git a/src/osd/modules/render/bgfx/valueuniformreader.cpp b/src/osd/modules/render/bgfx/valueuniformreader.cpp index e71f1c86329..137a6d24719 100644 --- a/src/osd/modules/render/bgfx/valueuniformreader.cpp +++ b/src/osd/modules/render/bgfx/valueuniformreader.cpp @@ -11,7 +11,7 @@ #include "entryuniform.h" #include "valueuniform.h" -bgfx_entry_uniform* value_uniform_reader::read_from_value(const Value& value, std::string prefix, bgfx_uniform* uniform) +bgfx_entry_uniform* value_uniform_reader::read_from_value(const Value& value, const std::string &prefix, bgfx_uniform* uniform) { if (!validate_parameters(value, prefix)) { @@ -36,9 +36,9 @@ bgfx_entry_uniform* value_uniform_reader::read_from_value(const Value& value, st return new bgfx_value_uniform(uniform, values, count); } -bool value_uniform_reader::validate_parameters(const Value& value, std::string prefix) +bool value_uniform_reader::validate_parameters(const Value& value, const std::string &prefix) { - if (!READER_CHECK(value.HasMember("value"), (prefix + "Must have string value 'value' (what value is being assigned?)\n").c_str())) return false; - if (!READER_CHECK(value["value"].IsArray() || value["value"].IsNumber(), (prefix + "Value 'value' must be numeric or an array\n").c_str())) return false; + if (!READER_CHECK(value.HasMember("value"), "%sMust have string value 'value' (what value is being assigned?)\n", prefix)) return false; + if (!READER_CHECK(value["value"].IsArray() || value["value"].IsNumber(), "%sValue 'value' must be numeric or an array\n", prefix)) return false; return true; } diff --git a/src/osd/modules/render/bgfx/valueuniformreader.h b/src/osd/modules/render/bgfx/valueuniformreader.h index 0b6fead1cca..ea544266a84 100644 --- a/src/osd/modules/render/bgfx/valueuniformreader.h +++ b/src/osd/modules/render/bgfx/valueuniformreader.h @@ -6,15 +6,14 @@ // //================================================================ +#ifndef MAME_RENDER_BGFX_VALUEUNIFORMREADER_H +#define MAME_RENDER_BGFX_VALUEUNIFORMREADER_H + #pragma once -#ifndef __DRAWBGFX_VALUE_UNIFORM_READER__ -#define __DRAWBGFX_VALUE_UNIFORM_READER__ +#include "statereader.h" #include <string> -#include <map> - -#include "statereader.h" class bgfx_entry_uniform; class bgfx_uniform; @@ -22,10 +21,10 @@ class bgfx_uniform; class value_uniform_reader : public state_reader { public: - static bgfx_entry_uniform* read_from_value(const Value& value, std::string prefix, bgfx_uniform* uniform); + static bgfx_entry_uniform* read_from_value(const Value& value, const std::string &prefix, bgfx_uniform* uniform); private: - static bool validate_parameters(const Value& value, std::string prefix); + static bool validate_parameters(const Value& value, const std::string &prefix); }; -#endif // __DRAWBGFX_VALUE_UNIFORM_READER__ +#endif // MAME_RENDER_BGFX_VALUEUNIFORMREADER_H diff --git a/src/osd/modules/render/bgfx/view.cpp b/src/osd/modules/render/bgfx/view.cpp index 6935e19bca7..3a731fc71f8 100644 --- a/src/osd/modules/render/bgfx/view.cpp +++ b/src/osd/modules/render/bgfx/view.cpp @@ -13,9 +13,7 @@ #include "target.h" void bgfx_view::update() { - std::shared_ptr<osd_window> win = m_renderer->assert_window(); - - const uint32_t window_index = win->index(); + const uint32_t window_index = m_renderer->window().index(); const uint32_t width = m_renderer->get_window_width(window_index); const uint32_t height = m_renderer->get_window_height(window_index); diff --git a/src/osd/modules/render/bgfx/view.h b/src/osd/modules/render/bgfx/view.h index a1793f89d0b..b257eb12ea4 100644 --- a/src/osd/modules/render/bgfx/view.h +++ b/src/osd/modules/render/bgfx/view.h @@ -4,12 +4,14 @@ #pragma once #include "window.h" + #include <climits> class renderer_bgfx; class bgfx_target; -class bgfx_view { +class bgfx_view +{ public: bgfx_view(renderer_bgfx *renderer, uint32_t index, bgfx_target *backbuffer, std::vector<uint32_t> &seen_views) : m_renderer(renderer) @@ -21,7 +23,7 @@ public: , m_view_height(0) , m_z_near(0.0f) , m_z_far(100.0f) - , m_clear_color(0) + , m_clear_color(0x000000ff) , m_clear_depth(1.0f) , m_clear_stencil(0) , m_do_clear_color(true) diff --git a/src/osd/modules/render/bgfx/windowparameter.h b/src/osd/modules/render/bgfx/windowparameter.h index 0ba4224de40..45df9060c71 100644 --- a/src/osd/modules/render/bgfx/windowparameter.h +++ b/src/osd/modules/render/bgfx/windowparameter.h @@ -6,22 +6,20 @@ // //============================================================ -#pragma once +#ifndef MAME_RENDER_BGFX_WINDOWPARAMETER_H +#define MAME_RENDER_BGFX_WINDOWPARAMETER_H -#ifndef __DRAWBGFX_WINDOW_PARAMETER__ -#define __DRAWBGFX_WINDOW_PARAMETER__ +#pragma once -#include <bgfx/bgfx.h> +#include "parameter.h" #include <string> - -#include "parameter.h" +#include <utility> class bgfx_window_parameter : public bgfx_parameter { public: - bgfx_window_parameter(std::string name, parameter_type type, uint32_t index) : bgfx_parameter(name, type), m_index(index) { } - virtual ~bgfx_window_parameter() { } + bgfx_window_parameter(std::string &&name, parameter_type type, uint32_t index) : bgfx_parameter(std::move(name), type), m_index(index) { } virtual float value() override { return float(m_index); } virtual void tick(double delta) override { } @@ -30,4 +28,4 @@ private: uint32_t m_index; }; -#endif // __DRAWBGFX_WINDOW_PARAMETER__ +#endif // MAME_RENDER_BGFX_WINDOWPARAMETER_H diff --git a/src/osd/modules/render/bgfxutil.cpp b/src/osd/modules/render/bgfxutil.cpp index e864cb98fb4..3d555bb60ad 100644 --- a/src/osd/modules/render/bgfxutil.cpp +++ b/src/osd/modules/render/bgfxutil.cpp @@ -14,10 +14,11 @@ #include "render.h" -const bgfx::Memory* bgfx_util::mame_texture_data_to_bgfx_texture_data(bgfx::TextureFormat::Enum &dst_format, uint32_t src_format, int rowpixels, int height, const rgb_t *palette, void *base, uint16_t &out_pitch, int &width_div_factor, int &width_mul_factor) +const bgfx::Memory* bgfx_util::mame_texture_data_to_bgfx_texture_data(bgfx::TextureFormat::Enum &dst_format, uint32_t src_format, int rowpixels, int width_margin, int height, const rgb_t *palette, void *base, uint16_t &out_pitch, int &width_div_factor, int &width_mul_factor) { bgfx::TextureInfo info; const bgfx::Memory *data = nullptr; + uint8_t *adjusted_base = (uint8_t *)base; switch (src_format) { @@ -28,7 +29,11 @@ const bgfx::Memory* bgfx_util::mame_texture_data_to_bgfx_texture_data(bgfx::Text out_pitch = rowpixels * 2; bgfx::calcTextureSize(info, rowpixels / width_div_factor, height, 1, false, false, 1, dst_format); - data = bgfx::copy(base, info.storageSize); + if (width_margin) + { + adjusted_base -= width_margin * 2; + } + data = bgfx::copy(adjusted_base, info.storageSize); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTE16): dst_format = bgfx::TextureFormat::R8; @@ -37,7 +42,11 @@ const bgfx::Memory* bgfx_util::mame_texture_data_to_bgfx_texture_data(bgfx::Text out_pitch = rowpixels * 2; bgfx::calcTextureSize(info, rowpixels * width_mul_factor, height, 1, false, false, 1, dst_format); - data = bgfx::copy(base, info.storageSize); + if (width_margin) + { + adjusted_base -= width_margin * 2; + } + data = bgfx::copy(adjusted_base, info.storageSize); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32): case PRIMFLAG_TEXFORMAT(TEXFORMAT_RGB32): @@ -47,7 +56,11 @@ const bgfx::Memory* bgfx_util::mame_texture_data_to_bgfx_texture_data(bgfx::Text out_pitch = rowpixels * 4; bgfx::calcTextureSize(info, rowpixels, height, 1, false, false, 1, dst_format); - data = bgfx::copy(base, info.storageSize); + if (width_margin) + { + adjusted_base -= width_margin * 4; + } + data = bgfx::copy(adjusted_base, info.storageSize); break; } @@ -96,6 +109,7 @@ uint64_t bgfx_util::get_blend_state(uint32_t blend) case BLENDMODE_ALPHA: return BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA); case BLENDMODE_RGB_MULTIPLY: + //return BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_DST_COLOR, BGFX_STATE_BLEND_INV_SRC_COLOR, BGFX_STATE_BLEND_ONE, BGFX_STATE_BLEND_ZERO); return BGFX_STATE_BLEND_FUNC_SEPARATE(BGFX_STATE_BLEND_DST_COLOR, BGFX_STATE_BLEND_ZERO, BGFX_STATE_BLEND_DST_ALPHA, BGFX_STATE_BLEND_ZERO); case BLENDMODE_ADD: return BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_ONE); @@ -104,3 +118,15 @@ uint64_t bgfx_util::get_blend_state(uint32_t blend) } return 0L; } + +void bgfx_util::find_prescale_factor(uint16_t width, uint16_t height, uint16_t max_prescale_size, uint16_t &xprescale, uint16_t &yprescale) +{ + while (xprescale > 1 && width * xprescale > max_prescale_size) + { + xprescale--; + } + while (yprescale > 1 && height * yprescale > max_prescale_size) + { + yprescale--; + } +} diff --git a/src/osd/modules/render/bgfxutil.h b/src/osd/modules/render/bgfxutil.h index db07e7fba77..aa108a6fb3b 100644 --- a/src/osd/modules/render/bgfxutil.h +++ b/src/osd/modules/render/bgfxutil.h @@ -15,9 +15,10 @@ class bgfx_util { public: - static const bgfx::Memory* mame_texture_data_to_bgfx_texture_data(bgfx::TextureFormat::Enum &dst_format, uint32_t format, int rowpixels, int height, const rgb_t *palette, void *base, uint16_t &out_pitch, int &width_div_factor, int &width_mul_factor); + static const bgfx::Memory* mame_texture_data_to_bgfx_texture_data(bgfx::TextureFormat::Enum &dst_format, uint32_t format, int rowpixels, int width_margin, int height, const rgb_t *palette, void *base, uint16_t &out_pitch, int &width_div_factor, int &width_mul_factor); static const bgfx::Memory* mame_texture_data_to_bgra32(uint32_t src_format, int width, int height, int rowpixels, const rgb_t *palette, void *base); static uint64_t get_blend_state(uint32_t blend); + static void find_prescale_factor(uint16_t width, uint16_t height, uint16_t max_prescale_size, uint16_t &xprescale, uint16_t &yprescale); }; #endif // RENDER_BGFX_UTIL diff --git a/src/osd/modules/render/binpacker.h b/src/osd/modules/render/binpacker.h index 1fb84596eec..52efe4abb85 100644 --- a/src/osd/modules/render/binpacker.h +++ b/src/osd/modules/render/binpacker.h @@ -29,7 +29,7 @@ public: class packable_rectangle { public: - packable_rectangle() : m_hash(0), m_width(-1), m_height(-1) { } + packable_rectangle() = default; packable_rectangle(uint32_t hash, uint32_t format, int width, int height, int rowpixels, const rgb_t *palette, void *base) : m_hash(hash) , m_format(format) @@ -50,19 +50,19 @@ public: void* base() const { return m_base; } private: - uint32_t m_hash; - uint32_t m_format; - int m_width; - int m_height; - int m_rowpixels; - const rgb_t* m_palette; - void* m_base; + uint32_t m_hash = 0; + uint32_t m_format = 0; + int m_width = -1; + int m_height = -1; + int m_rowpixels = 0; + const rgb_t* m_palette = nullptr; + void* m_base = nullptr; }; class packed_rectangle { public: - packed_rectangle() : m_hash(0), m_format(0), m_width(-1), m_height(-1), m_x(-1), m_y(-1), m_rowpixels(0), m_palette(nullptr), m_base(nullptr) { } + packed_rectangle() = default; packed_rectangle(const packed_rectangle& rect) : m_hash(rect.m_hash) , m_format(rect.m_format) @@ -99,15 +99,15 @@ public: void* base() const { return m_base; } private: - uint32_t m_hash; - uint32_t m_format; - int m_width; - int m_height; - int m_x; - int m_y; - int m_rowpixels; - const rgb_t* m_palette; - void* m_base; + uint32_t m_hash = 0; + uint32_t m_format = 0; + int m_width = -1; + int m_height = -1; + int m_x = -1; + int m_y = -1; + int m_rowpixels = 0; + const rgb_t* m_palette = nullptr; + void* m_base = nullptr; }; bool pack(const std::vector<packable_rectangle>& rects, std::vector<std::vector<packed_rectangle>>& packs, int pack_size); diff --git a/src/osd/modules/render/blit13.h b/src/osd/modules/render/blit13.ipp index 1ac485bab8d..1ac485bab8d 100644 --- a/src/osd/modules/render/blit13.h +++ b/src/osd/modules/render/blit13.ipp diff --git a/src/osd/modules/render/d3d/d3dcomm.h b/src/osd/modules/render/d3d/d3dcomm.h index c7dc58c6340..846d9dac791 100644 --- a/src/osd/modules/render/d3d/d3dcomm.h +++ b/src/osd/modules/render/d3d/d3dcomm.h @@ -6,8 +6,22 @@ // //============================================================ -#ifndef __WIN_D3DCOMM__ -#define __WIN_D3DCOMM__ +#ifndef MAME_RENDER_D3D_D3DCOMM_H +#define MAME_RENDER_D3D_D3DCOMM_H + +#pragma once + +// lib/util +#include "bitmap.h" + +#include <windows.h> +#include <d3d9.h> +#include <wrl/client.h> + +#include <cstring> +#include <memory> +#include <vector> + //============================================================ // CONSTANTS @@ -60,10 +74,7 @@ public: class d3d_texture_manager { public: - d3d_texture_manager(): m_renderer(nullptr), m_yuv_format(), m_texture_caps(0), m_texture_max_aspect(0), m_texture_max_width(0), m_texture_max_height(0), m_default_texture(nullptr) - { } - - d3d_texture_manager(renderer_d3d9 *d3d); + d3d_texture_manager(renderer_d3d9 &d3d, IDirect3D9 *d3dobj); void update_textures(); @@ -82,12 +93,12 @@ public: texture_info * get_default_texture() const { return m_default_texture; } - renderer_d3d9 * get_d3d() const { return m_renderer; } + renderer_d3d9 & get_d3d() const { return m_renderer; } - std::vector<std::unique_ptr<texture_info>> m_texture_list; // list of active textures + std::vector<std::unique_ptr<texture_info> > m_texture_list; // list of active textures private: - renderer_d3d9 * m_renderer; + renderer_d3d9 & m_renderer; D3DFORMAT m_yuv_format; // format to use for YUV textures DWORD m_texture_caps; // textureCaps field @@ -104,7 +115,7 @@ private: class texture_info { public: - texture_info(d3d_texture_manager *manager, const render_texinfo *texsource, int prescale, uint32_t flags); + texture_info(d3d_texture_manager &manager, const render_texinfo *texsource, int prescale, uint32_t flags); ~texture_info(); render_texinfo & get_texinfo() { return m_texinfo; } @@ -125,15 +136,16 @@ public: int get_cur_frame() const { return m_cur_frame; } - IDirect3DTexture9 * get_tex() const { return m_d3dtex; } - IDirect3DSurface9 * get_surface() const { return m_d3dsurface; } - IDirect3DTexture9 * get_finaltex() const { return m_d3dfinaltex; } + IDirect3DTexture9 * get_finaltex() const { return m_d3dfinaltex.Get(); } vec2f & get_uvstart() { return m_start; } vec2f & get_uvstop() { return m_stop; } vec2f & get_rawdims() { return m_rawdims; } private: + using IDirect3DTexture9Ptr = Microsoft::WRL::ComPtr<IDirect3DTexture9>; + using IDirect3DSurface9Ptr = Microsoft::WRL::ComPtr<IDirect3DSurface9>; + void prescale(); void compute_size(int texwidth, int texheight); void compute_size_subroutine(int texwidth, int texheight, int* p_width, int* p_height); @@ -145,23 +157,23 @@ private: inline void copyline_yuy16_to_uyvy(uint16_t *dst, const uint16_t *src, int width, const rgb_t *palette); inline void copyline_yuy16_to_argb(uint32_t *dst, const uint16_t *src, int width, const rgb_t *palette); - d3d_texture_manager * m_texture_manager; // texture manager pointer + d3d_texture_manager & m_texture_manager; // texture manager pointer - renderer_d3d9 * m_renderer; // renderer pointer + renderer_d3d9 & m_renderer; // renderer pointer - uint32_t m_hash; // hash value for the texture - uint32_t m_flags; // rendering flags + const uint32_t m_hash; // hash value for the texture + const uint32_t m_flags; // rendering flags render_texinfo m_texinfo; // copy of the texture info + const int m_type; // what type of texture are we? vec2f m_start; // beggining UV coordinates vec2f m_stop; // ending UV coordinates vec2f m_rawdims; // raw dims of the texture - int m_type; // what type of texture are we? int m_xprescale, m_yprescale; // X/Y prescale factor int m_xborderpix, m_yborderpix; // X/Y border pixels int m_cur_frame; // what is our current frame? - IDirect3DTexture9 * m_d3dtex; // Direct3D texture pointer - IDirect3DSurface9 * m_d3dsurface; // Direct3D offscreen plain surface pointer - IDirect3DTexture9 * m_d3dfinaltex; // Direct3D final (post-scaled) texture + IDirect3DTexture9Ptr m_d3dtex; // Direct3D texture pointer + IDirect3DSurface9Ptr m_d3dsurface; // Direct3D offscreen plain surface pointer + IDirect3DTexture9Ptr m_d3dfinaltex; // Direct3D final (post-scaled) texture }; /* poly_info holds information about a single polygon/d3d primitive */ @@ -170,7 +182,7 @@ class poly_info public: void init(D3DPRIMITIVETYPE type, uint32_t count, uint32_t numverts, uint32_t flags, texture_info *texture, uint32_t modmode, - float prim_width, float prim_height) + float prim_width, float prim_height, uint32_t tint) { m_type = type; m_count = count; @@ -180,6 +192,7 @@ public: m_modmode = modmode; m_prim_width = prim_width; m_prim_height = prim_height; + m_tint = tint; } D3DPRIMITIVETYPE type() const { return m_type; } @@ -193,6 +206,8 @@ public: float prim_width() const { return m_prim_width; } float prim_height() const { return m_prim_height; } + DWORD tint() const { return m_tint; } + private: D3DPRIMITIVETYPE m_type; // type of primitive uint32_t m_count; // total number of primitives @@ -204,6 +219,8 @@ private: float m_prim_width; // used by quads float m_prim_height; // used by quads + + uint32_t m_tint; // color tint for primitive }; /* vertex describes a single vertex */ @@ -222,24 +239,14 @@ class d3d_render_target { public: // construction/destruction - d3d_render_target(): target_width(0), target_height(0), width(0), height(0), screen_index(0), bloom_count(0) + d3d_render_target() + : target_width(0) + , target_height(0) + , width(0) + , height(0) + , screen_index(0) + , bloom_count(0) { - for (int index = 0; index < MAX_BLOOM_COUNT; index++) - { - bloom_texture[index] = nullptr; - bloom_surface[index] = nullptr; - } - - for (int index = 0; index < 2; index++) - { - source_texture[index] = nullptr; - source_surface[index] = nullptr; - target_texture[index] = nullptr; - target_surface[index] = nullptr; - } - - cache_texture = nullptr; - cache_surface = nullptr; } ~d3d_render_target(); @@ -257,20 +264,20 @@ public: int screen_index; - IDirect3DSurface9 *target_surface[2]; - IDirect3DTexture9 *target_texture[2]; - IDirect3DSurface9 *source_surface[2]; - IDirect3DTexture9 *source_texture[2]; + Microsoft::WRL::ComPtr<IDirect3DSurface9> target_surface[2]; + Microsoft::WRL::ComPtr<IDirect3DTexture9> target_texture[2]; + Microsoft::WRL::ComPtr<IDirect3DSurface9> source_surface[2]; + Microsoft::WRL::ComPtr<IDirect3DTexture9> source_texture[2]; - IDirect3DSurface9 *cache_surface; - IDirect3DTexture9 *cache_texture; + Microsoft::WRL::ComPtr<IDirect3DSurface9> cache_surface; + Microsoft::WRL::ComPtr<IDirect3DTexture9> cache_texture; - IDirect3DSurface9 *bloom_surface[MAX_BLOOM_COUNT]; - IDirect3DTexture9 *bloom_texture[MAX_BLOOM_COUNT]; + Microsoft::WRL::ComPtr<IDirect3DSurface9> bloom_surface[MAX_BLOOM_COUNT]; + Microsoft::WRL::ComPtr<IDirect3DTexture9> bloom_texture[MAX_BLOOM_COUNT]; float bloom_dims[MAX_BLOOM_COUNT][2]; int bloom_count; }; -#endif +#endif // MAME_RENDER_D3D_D3DCOMM_H diff --git a/src/osd/modules/render/d3d/d3dhlsl.cpp b/src/osd/modules/render/d3d/d3dhlsl.cpp index 22a6b5af508..52bec85f06f 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.cpp +++ b/src/osd/modules/render/d3d/d3dhlsl.cpp @@ -8,15 +8,18 @@ // MAME headers #include "emu.h" + #include "drivenum.h" +#include "emuopts.h" +#include "fileio.h" +#include "main.h" #include "render.h" #include "rendlay.h" #include "rendutil.h" -#include "emuopts.h" -#include "fileio.h" +#include "screen.h" + #include "aviio.h" #include "png.h" -#include "screen.h" // MAMEOS headers #include "winmain.h" @@ -50,8 +53,6 @@ class movie_recorder public: movie_recorder(running_machine& machine, renderer_d3d9 *d3d, int width, int height) : m_initialized(false), m_d3d(d3d), m_width(width), m_height(height) - , m_sys_texture(nullptr), m_sys_surface(nullptr) - , m_vid_texture(nullptr), m_vid_surface(nullptr) { HRESULT result; @@ -82,17 +83,6 @@ public: ~movie_recorder() { - if (m_sys_texture != nullptr) - m_sys_texture->Release(); - - if (m_sys_surface != nullptr) - m_sys_surface->Release(); - - if (m_vid_texture != nullptr) - m_vid_texture->Release(); - - if (m_vid_surface != nullptr) - m_vid_surface->Release(); } void record(const char *name) @@ -109,7 +99,7 @@ public: return; // copy the frame from video memory, where it is not accessible, to system memory - HRESULT result = m_d3d->get_device()->GetRenderTargetData(m_vid_surface, m_sys_surface); + HRESULT result = m_d3d->get_device()->GetRenderTargetData(m_vid_surface.Get(), m_sys_surface.Get()); if (FAILED(result)) return; @@ -144,7 +134,7 @@ public: m_avi_writer->audio_frame(buffer, samples_this_frame); } - IDirect3DSurface9 * target_surface() { return m_vid_surface; } + IDirect3DSurface9 * target_surface() { return m_vid_surface.Get(); } private: bool m_initialized; @@ -156,10 +146,10 @@ private: bitmap_rgb32 m_frame; int m_width; int m_height; - IDirect3DTexture9 * m_sys_texture; // texture in system memory - IDirect3DSurface9 * m_sys_surface; // surface in system memory - IDirect3DTexture9 * m_vid_texture; // texture in video memory - IDirect3DSurface9 * m_vid_surface; // surface in video memory + Microsoft::WRL::ComPtr<IDirect3DTexture9> m_sys_texture; // texture in system memory + Microsoft::WRL::ComPtr<IDirect3DSurface9> m_sys_surface; // surface in system memory + Microsoft::WRL::ComPtr<IDirect3DTexture9> m_vid_texture; // texture in video memory + Microsoft::WRL::ComPtr<IDirect3DSurface9> m_vid_surface; // surface in video memory }; @@ -168,51 +158,32 @@ private: //============================================================ shaders::shaders() : - d3dintf(nullptr), machine(nullptr), d3d(nullptr), post_fx_enable(false), oversampling_enable(false), num_screens(0), - curr_screen(0), + num_targets(0), + curr_target(0), acc_t(0), delta_t(0), shadow_texture(nullptr), lut_texture(nullptr), ui_lut_texture(nullptr), options(nullptr), - black_surface(nullptr), - black_texture(nullptr), recording_movie(false), render_snap(false), - snap_copy_target(nullptr), - snap_copy_texture(nullptr), - snap_target(nullptr), - snap_texture(nullptr), snap_width(0), snap_height(0), initialized(false), - backbuffer(nullptr), curr_effect(nullptr), - default_effect(nullptr), - prescale_effect(nullptr), - post_effect(nullptr), - distortion_effect(nullptr), - scanline_effect(nullptr), - focus_effect(nullptr), - phosphor_effect(nullptr), - deconverge_effect(nullptr), - color_effect(nullptr), - ntsc_effect(nullptr), - bloom_effect(nullptr), - downsample_effect(nullptr), - vector_effect(nullptr), - chroma_effect(nullptr), + diffuse_texture(nullptr), curr_texture(nullptr), curr_render_target(nullptr), curr_poly(nullptr), d3dx_create_effect_from_file_ptr(nullptr) { + std::fill(std::begin(target_to_screen), std::end(target_to_screen), 0); } @@ -222,12 +193,7 @@ shaders::shaders() : shaders::~shaders() { - for (slider* slider : internal_sliders) - { - delete slider; - } - - if (options != nullptr) + if (options && (&last_options != options)) { delete options; options = nullptr; @@ -244,11 +210,9 @@ void shaders::save_snapshot() if (!enabled()) return; - auto win = d3d->assert_window(); - int width = snap_width; int height = snap_height; - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(width, height); } @@ -289,8 +253,7 @@ void shaders::record_movie() return; } - auto win = d3d->assert_window(); - osd_dim wdim = win->get_size(); + osd_dim wdim = d3d->window().get_size(); recorder = std::make_unique<movie_recorder>(*machine, d3d, wdim.width(), wdim.height()); recorder->record(downcast<windows_options &>(machine->options()).d3d_hlsl_write()); @@ -323,11 +286,9 @@ void shaders::render_snapshot(IDirect3DSurface9 *surface) if (!enabled()) return; - auto win = d3d->assert_window(); - int width = snap_width; int height = snap_height; - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(width, height); } @@ -337,7 +298,7 @@ void shaders::render_snapshot(IDirect3DSurface9 *surface) return; // copy the texture - HRESULT result = d3d->get_device()->GetRenderTargetData(surface, snap_copy_target); + HRESULT result = d3d->get_device()->GetRenderTargetData(surface, snap_copy_target.Get()); if (FAILED(result)) return; @@ -378,29 +339,10 @@ void shaders::render_snapshot(IDirect3DSurface9 *surface) if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during texture UnlockRect call\n", result); - if (snap_texture != nullptr) - { - snap_texture->Release(); - snap_texture = nullptr; - } - - if (snap_target != nullptr) - { - snap_target->Release(); - snap_target = nullptr; - } - - if (snap_copy_texture != nullptr) - { - snap_copy_texture->Release(); - snap_copy_texture = nullptr; - } - - if (snap_copy_target != nullptr) - { - snap_copy_target->Release(); - snap_copy_target = nullptr; - } + snap_texture.Reset(); + snap_target.Reset(); + snap_copy_texture.Reset(); + snap_copy_target.Reset(); } @@ -440,25 +382,33 @@ void shaders::set_texture(texture_info *texture) return; } - // set initial texture to use - texture_info *default_texture = d3d->get_default_texture(); - default_effect->set_texture("Diffuse", (texture == nullptr) ? default_texture->get_finaltex() : texture->get_finaltex()); - if (options->yiq_enable) + if (texture) { - ntsc_effect->set_texture("Diffuse", (texture == nullptr) ? default_texture->get_finaltex() : texture->get_finaltex()); + diffuse_texture = texture; } else { - color_effect->set_texture("Diffuse", (texture == nullptr) ? default_texture->get_finaltex() : texture->get_finaltex()); + diffuse_texture = d3d->get_default_texture(); } } //============================================================ +// shaders::set_filter - indicates if screens should be +// filtered on upscaling +//============================================================ + +void shaders::set_filter(bool filter_screens) +{ + this->filter_screens = filter_screens; +} + + +//============================================================ // shaders::init //============================================================ -bool shaders::init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *renderer) +bool shaders::init(IDirect3D9 *d3dobj, running_machine *machine, renderer_d3d9 *renderer) { osd_printf_verbose("Direct3D: Initialize HLSL\n"); @@ -490,14 +440,10 @@ bool shaders::init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *r return false; } - d3dintf->post_fx_available = true; - - this->d3dintf = d3dintf; + this->d3dobj = d3dobj; this->machine = machine; this->d3d = renderer; - enumerate_screens(); - auto &winoptions = downcast<windows_options &>(machine->options()); post_fx_enable = winoptions.d3d_hlsl_enable(); @@ -505,19 +451,18 @@ bool shaders::init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *r snap_width = winoptions.d3d_snap_width(); snap_height = winoptions.d3d_snap_height(); - this->options = new hlsl_options; - this->options->params_init = false; - - // copy last options if initialized if (last_options.params_init) { + // copy last options if initialized osd_printf_verbose("Direct3D: First restore options\n"); options = &last_options; } - - // read options if not initialized - if (!options->params_init) + else { + // read options if not initialized + options = new hlsl_options; + options->params_init = false; + strncpy(options->shadow_mask_texture, winoptions.screen_shadow_mask_texture(), sizeof(options->shadow_mask_texture)); options->shadow_mask_tile_mode = winoptions.screen_shadow_mask_tile_mode(); options->shadow_mask_alpha = winoptions.screen_shadow_mask_alpha(); @@ -599,6 +544,7 @@ bool shaders::init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *r osd_printf_verbose("Direct3D: First store options\n"); last_options = *options; + delete options; options = &last_options; } @@ -613,11 +559,70 @@ bool shaders::init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *r //============================================================ +// shaders::begin_frame +// +// Enumerates the total number of screen textures present. +// +// Additionally, ensures the presence of necessary post- +// processing geometry. +// +//============================================================ + +void shaders::begin_frame(render_primitive_list *primlist) +{ + init_fsfx_quad(); + + std::fill(std::begin(target_to_screen), std::end(target_to_screen), 0); + std::fill(std::begin(targets_per_screen), std::end(targets_per_screen), 0); + EQUIVALENT_ARRAY(target_to_screen, render_container *) containers; + + // Maximum potential runtime O(max_num_targets^2) + num_targets = 0; + num_screens = 0; + curr_target = 0; + for (render_primitive &prim : *primlist) + { + if (PRIMFLAG_GET_SCREENTEX(prim.flags)) + { + int screen_index = 0; + for (; screen_index < num_screens && containers[screen_index] != prim.container; screen_index++); + containers[screen_index] = prim.container; + target_to_screen[num_targets] = screen_index; + targets_per_screen[screen_index]++; + if (screen_index >= num_screens) + num_screens++; + num_targets++; + } + } + + diffuse_texture = d3d->get_default_texture(); +} + + +//============================================================ +// shaders::end_frame +// +// Closes out any still-active effects at the end of +// rendering. +// +//============================================================ + +void shaders::end_frame() +{ + if (curr_effect->is_active()) + { + curr_effect->end(); + } +} + + +//============================================================ // shaders::init_fsfx_quad // // Called always at the start of each frame so that the two // triangles used for the post-processing effects are always -// at the beginning of the vertex buffer +// at the beginning of the vertex buffer. +// //============================================================ void shaders::init_fsfx_quad() @@ -714,7 +719,7 @@ int shaders::create_resources() return 1; } black_texture->GetSurfaceLevel(0, &black_surface); - result = d3d->get_device()->SetRenderTarget(0, black_surface); + result = d3d->get_device()->SetRenderTarget(0, black_surface.Get()); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call\n", result); @@ -722,7 +727,7 @@ int shaders::create_resources() if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device clear call\n", result); - result = d3d->get_device()->SetRenderTarget(0, backbuffer); + result = d3d->get_device()->SetRenderTarget(0, backbuffer.Get()); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call\n", result); @@ -747,7 +752,7 @@ int shaders::create_resources() texture.seqid = 0; // now create it (no prescale, no wrap) - auto tex = std::make_unique<texture_info>(d3d->get_texture_manager(), &texture, 1, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); + auto tex = std::make_unique<texture_info>(*d3d->get_texture_manager(), &texture, 1, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); shadow_texture = tex.get(); d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } @@ -770,7 +775,7 @@ int shaders::create_resources() texture.seqid = 0; // now create it (no prescale, no wrap) - auto tex = std::make_unique<texture_info>(d3d->get_texture_manager(), &texture, 1, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); + auto tex = std::make_unique<texture_info>(*d3d->get_texture_manager(), &texture, 1, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); lut_texture = tex.get(); d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } @@ -793,74 +798,66 @@ int shaders::create_resources() texture.seqid = 0; // now create it (no prescale, no wrap) - auto tex = std::make_unique<texture_info>(d3d->get_texture_manager(), &texture, 1, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); + auto tex = std::make_unique<texture_info>(*d3d->get_texture_manager(), &texture, 1, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); ui_lut_texture = tex.get(); d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } const char *fx_dir = downcast<windows_options &>(machine->options()).screen_post_fx_dir(); - default_effect = new effect(this, d3d->get_device(), "primary.fx", fx_dir); - post_effect = new effect(this, d3d->get_device(), "post.fx", fx_dir); - distortion_effect = new effect(this, d3d->get_device(), "distortion.fx", fx_dir); - prescale_effect = new effect(this, d3d->get_device(), "prescale.fx", fx_dir); - phosphor_effect = new effect(this, d3d->get_device(), "phosphor.fx", fx_dir); - focus_effect = new effect(this, d3d->get_device(), "focus.fx", fx_dir); - scanline_effect = new effect(this, d3d->get_device(), "scanline.fx", fx_dir); - deconverge_effect = new effect(this, d3d->get_device(), "deconverge.fx", fx_dir); - color_effect = new effect(this, d3d->get_device(), "color.fx", fx_dir); - ntsc_effect = new effect(this, d3d->get_device(), "ntsc.fx", fx_dir); - bloom_effect = new effect(this, d3d->get_device(), "bloom.fx", fx_dir); - downsample_effect = new effect(this, d3d->get_device(), "downsample.fx", fx_dir); - vector_effect = new effect(this, d3d->get_device(), "vector.fx", fx_dir); - chroma_effect = new effect(this, d3d->get_device(), "chroma.fx", fx_dir); - - if (!default_effect->is_valid() || - !post_effect->is_valid() || - !distortion_effect->is_valid() || - !prescale_effect->is_valid() || - !phosphor_effect->is_valid() || - !focus_effect->is_valid() || - !scanline_effect->is_valid() || - !deconverge_effect->is_valid() || - !color_effect->is_valid() || - !ntsc_effect->is_valid() || - !bloom_effect->is_valid() || - !downsample_effect->is_valid() || - !vector_effect->is_valid() || - !chroma_effect->is_valid()) - { - return 1; - } - - std::array<effect*, 15> effects = { - default_effect, - post_effect, - distortion_effect, - prescale_effect, - phosphor_effect, - focus_effect, - scanline_effect, - deconverge_effect, - color_effect, - ntsc_effect, - color_effect, - bloom_effect, - downsample_effect, - vector_effect, - chroma_effect - }; - - for (int i = 0; i < effects.size(); i++) - { - effects[i]->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); - effects[i]->add_uniform("TargetDims", uniform::UT_VEC2, uniform::CU_TARGET_DIMS); - effects[i]->add_uniform("TargetScale", uniform::UT_FLOAT, uniform::CU_TARGET_SCALE); - effects[i]->add_uniform("ScreenDims", uniform::UT_VEC2, uniform::CU_SCREEN_DIMS); - effects[i]->add_uniform("ScreenCount", uniform::UT_INT, uniform::CU_SCREEN_COUNT); - effects[i]->add_uniform("QuadDims", uniform::UT_VEC2, uniform::CU_QUAD_DIMS); - effects[i]->add_uniform("SwapXY", uniform::UT_BOOL, uniform::CU_SWAP_XY); - effects[i]->add_uniform("VectorScreen", uniform::UT_BOOL, uniform::CU_VECTOR_SCREEN); + default_effect = std::make_unique<effect>(this, d3d->get_device(), "primary.fx", fx_dir); + ui_effect = std::make_unique<effect>(this, d3d->get_device(), "primary.fx", fx_dir); + ui_wrap_effect = std::make_unique<effect>(this, d3d->get_device(), "primary.fx", fx_dir); + vector_buffer_effect = std::make_unique<effect>(this, d3d->get_device(), "primary.fx", fx_dir); + post_effect = std::make_unique<effect>(this, d3d->get_device(), "post.fx", fx_dir); + distortion_effect = std::make_unique<effect>(this, d3d->get_device(), "distortion.fx", fx_dir); + prescale_effect = std::make_unique<effect>(this, d3d->get_device(), "prescale.fx", fx_dir); + prescale_point_effect = std::make_unique<effect>(this, d3d->get_device(), "prescale.fx", fx_dir); + phosphor_effect = std::make_unique<effect>(this, d3d->get_device(), "phosphor.fx", fx_dir); + focus_effect = std::make_unique<effect>(this, d3d->get_device(), "focus.fx", fx_dir); + scanline_effect = std::make_unique<effect>(this, d3d->get_device(), "scanline.fx", fx_dir); + deconverge_effect = std::make_unique<effect>(this, d3d->get_device(), "deconverge.fx", fx_dir); + color_effect = std::make_unique<effect>(this, d3d->get_device(), "color.fx", fx_dir); + ntsc_effect = std::make_unique<effect>(this, d3d->get_device(), "ntsc.fx", fx_dir); + bloom_effect = std::make_unique<effect>(this, d3d->get_device(), "bloom.fx", fx_dir); + downsample_effect = std::make_unique<effect>(this, d3d->get_device(), "downsample.fx", fx_dir); + vector_effect = std::make_unique<effect>(this, d3d->get_device(), "vector.fx", fx_dir); + chroma_effect = std::make_unique<effect>(this, d3d->get_device(), "chroma.fx", fx_dir); + + std::array<effect*, 18> effects = { + default_effect.get(), + ui_effect.get(), + ui_wrap_effect.get(), + vector_buffer_effect.get(), + post_effect.get(), + distortion_effect.get(), + prescale_effect.get(), + prescale_point_effect.get(), + phosphor_effect.get(), + focus_effect.get(), + scanline_effect.get(), + deconverge_effect.get(), + color_effect.get(), + ntsc_effect.get(), + bloom_effect.get(), + downsample_effect.get(), + vector_effect.get(), + chroma_effect.get() }; + + for (effect *eff : effects) + { + if (!eff->is_valid()) + return 1; + } + + for (effect *eff : effects) + { + eff->add_uniform("TargetDims", uniform::UT_VEC2, uniform::CU_TARGET_DIMS); + eff->add_uniform("TargetScale", uniform::UT_FLOAT, uniform::CU_TARGET_SCALE); + eff->add_uniform("ScreenDims", uniform::UT_VEC2, uniform::CU_SCREEN_DIMS); + eff->add_uniform("ScreenCount", uniform::UT_INT, uniform::CU_SCREEN_COUNT); + eff->add_uniform("SwapXY", uniform::UT_BOOL, uniform::CU_SWAP_XY); + eff->add_uniform("VectorScreen", uniform::UT_BOOL, uniform::CU_VECTOR_SCREEN); } ntsc_effect->add_uniform("CCValue", uniform::UT_FLOAT, uniform::CU_NTSC_CCFREQ); @@ -873,6 +870,7 @@ int shaders::create_resources() ntsc_effect->add_uniform("IFreqResponse", uniform::UT_FLOAT, uniform::CU_NTSC_IFREQ); ntsc_effect->add_uniform("QFreqResponse", uniform::UT_FLOAT, uniform::CU_NTSC_QFREQ); ntsc_effect->add_uniform("ScanTime", uniform::UT_FLOAT, uniform::CU_NTSC_HTIME); + ntsc_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); color_effect->add_uniform("RedRatios", uniform::UT_VEC3, uniform::CU_COLOR_RED_RATIOS); color_effect->add_uniform("GrnRatios", uniform::UT_VEC3, uniform::CU_COLOR_GRN_RATIOS); @@ -880,6 +878,8 @@ int shaders::create_resources() color_effect->add_uniform("Offset", uniform::UT_VEC3, uniform::CU_COLOR_OFFSET); color_effect->add_uniform("Scale", uniform::UT_VEC3, uniform::CU_COLOR_SCALE); color_effect->add_uniform("Saturation", uniform::UT_FLOAT, uniform::CU_COLOR_SATURATION); + color_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); + color_effect->add_uniform("LutEnable", uniform::UT_BOOL, uniform::CU_LUT_ENABLE); deconverge_effect->add_uniform("ConvergeX", uniform::UT_VEC3, uniform::CU_CONVERGE_LINEAR_X); deconverge_effect->add_uniform("ConvergeY", uniform::UT_VEC3, uniform::CU_CONVERGE_LINEAR_Y); @@ -892,6 +892,8 @@ int shaders::create_resources() scanline_effect->add_uniform("ScanlineVariation", uniform::UT_FLOAT, uniform::CU_POST_SCANLINE_VARIATION); scanline_effect->add_uniform("ScanlineBrightScale", uniform::UT_FLOAT, uniform::CU_POST_SCANLINE_BRIGHT_SCALE); scanline_effect->add_uniform("ScanlineBrightOffset", uniform::UT_FLOAT, uniform::CU_POST_SCANLINE_BRIGHT_OFFSET); + scanline_effect->add_uniform("QuadDims", uniform::UT_VEC2, uniform::CU_QUAD_DIMS); + scanline_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); focus_effect->add_uniform("Defocus", uniform::UT_VEC2, uniform::CU_FOCUS_SIZE); @@ -904,9 +906,15 @@ int shaders::create_resources() post_effect->add_uniform("Floor", uniform::UT_VEC3, uniform::CU_POST_FLOOR); post_effect->add_uniform("ChomaMode", uniform::UT_INT, uniform::CU_CHROMA_MODE); post_effect->add_uniform("ConversionGain", uniform::UT_VEC3, uniform::CU_CHROMA_CONVERSION_GAIN); + post_effect->add_uniform("QuadDims", uniform::UT_VEC2, uniform::CU_QUAD_DIMS); + post_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); phosphor_effect->add_uniform("Phosphor", uniform::UT_VEC3, uniform::CU_PHOSPHOR_LIFE); + bloom_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); + + downsample_effect->add_uniform("QuadDims", uniform::UT_VEC2, uniform::CU_QUAD_DIMS); + chroma_effect->add_uniform("YGain", uniform::UT_VEC3, uniform::CU_CHROMA_Y_GAIN); chroma_effect->add_uniform("ChromaA", uniform::UT_VEC2, uniform::CU_CHROMA_A); chroma_effect->add_uniform("ChromaB", uniform::UT_VEC2, uniform::CU_CHROMA_B); @@ -919,9 +927,19 @@ int shaders::create_resources() distortion_effect->add_uniform("RoundCornerAmount", uniform::UT_FLOAT, uniform::CU_POST_ROUND_CORNER); distortion_effect->add_uniform("SmoothBorderAmount", uniform::UT_FLOAT, uniform::CU_POST_SMOOTH_BORDER); distortion_effect->add_uniform("ReflectionAmount", uniform::UT_FLOAT, uniform::CU_POST_REFLECTION); + distortion_effect->add_uniform("QuadDims", uniform::UT_VEC2, uniform::CU_QUAD_DIMS); + + prescale_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); + + prescale_point_effect->add_uniform("SourceDims", uniform::UT_VEC2, uniform::CU_SOURCE_DIMS); - default_effect->add_uniform("LutEnable", uniform::UT_BOOL, uniform::CU_LUT_ENABLE); - default_effect->add_uniform("UiLutEnable", uniform::UT_BOOL, uniform::CU_UI_LUT_ENABLE); + ui_effect->add_uniform("LutEnable", uniform::UT_BOOL, uniform::CU_LUT_ENABLE); + ui_effect->add_uniform("UiLutEnable", uniform::UT_BOOL, uniform::CU_UI_LUT_ENABLE); + + ui_wrap_effect->add_uniform("LutEnable", uniform::UT_BOOL, uniform::CU_LUT_ENABLE); + ui_wrap_effect->add_uniform("UiLutEnable", uniform::UT_BOOL, uniform::CU_UI_LUT_ENABLE); + + vector_buffer_effect->add_uniform("LutEnable", uniform::UT_BOOL, uniform::CU_LUT_ENABLE); return 0; } @@ -933,24 +951,24 @@ int shaders::create_resources() void shaders::begin_draw() { - double t; - if (!enabled()) - { return; - } - curr_screen = 0; - curr_effect = default_effect; + curr_target = 0; + // Update for delta_time - t = machine->time().as_double(); + const double t = machine->time().as_double(); delta_t = t - acc_t; acc_t = t; default_effect->set_technique("ScreenTechnique"); + ui_effect->set_technique("UiTechnique"); + ui_wrap_effect->set_technique("UiWrapTechnique"); + vector_buffer_effect->set_technique("VectorBufferTechnique"); post_effect->set_technique("DefaultTechnique"); distortion_effect->set_technique("DefaultTechnique"); prescale_effect->set_technique("DefaultTechnique"); + prescale_point_effect->set_technique("PointTechnique"); phosphor_effect->set_technique("DefaultTechnique"); focus_effect->set_technique("DefaultTechnique"); scanline_effect->set_technique("DefaultTechnique"); @@ -963,11 +981,31 @@ void shaders::begin_draw() vector_effect->set_technique("DefaultTechnique"); chroma_effect->set_technique("DefaultTechnique"); - HRESULT result = d3d->get_device()->SetRenderTarget(0, backbuffer); + HRESULT result = d3d->get_device()->SetRenderTarget(0, backbuffer.Get()); if (FAILED(result)) { osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call\n", result); } + + set_curr_effect(default_effect.get()); +} + + +//============================================================ +// shaders::set_curr_effect +//============================================================ + +void shaders::set_curr_effect(effect *curr_effect) +{ + if (this->curr_effect == curr_effect) + { + return; + } + if (this->curr_effect && this->curr_effect->is_active()) + { + this->curr_effect->end(); + } + this->curr_effect = curr_effect; } @@ -994,7 +1032,7 @@ void shaders::blit( if (clear_dst) { - result = d3d->get_device()->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_ARGB(1,0,0,0), 0, 0); + result = d3d->get_device()->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0xff,0,0,0), 0, 0); if (FAILED(result)) { osd_printf_verbose("Direct3D: Error %08lX during device clear call\n", result); @@ -1002,24 +1040,17 @@ void shaders::blit( } } - UINT num_passes = 0; - curr_effect->begin(&num_passes, 0); - - for (UINT pass = 0; pass < num_passes; pass++) + if (!curr_effect->is_active()) { - curr_effect->begin_pass(pass); - - // add the primitives - result = d3d->get_device()->DrawPrimitive(prim_type, prim_index, prim_count); - if (FAILED(result)) - { - osd_printf_verbose("Direct3D: Error %08lX during device DrawPrimitive call\n", result); - } - - curr_effect->end_pass(); + curr_effect->begin(0); } - curr_effect->end(); + // add the primitives + result = d3d->get_device()->DrawPrimitive(prim_type, prim_index, prim_count); + if (FAILED(result)) + { + osd_printf_verbose("Direct3D: Error %08lX during device DrawPrimitive call\n", result); + } } //============================================================ @@ -1056,14 +1087,15 @@ int shaders::ntsc_pass(d3d_render_target *rt, int source_index, poly_info *poly, // initial "Diffuse" texture is set in shaders::set_texture() - curr_effect = ntsc_effect; + set_curr_effect(ntsc_effect.get()); + curr_effect->set_texture("Diffuse", diffuse_texture->get_finaltex()); curr_effect->update_uniforms(); curr_effect->set_float("SignalOffset", signal_offset); next_index = rt->next_index(next_index); - blit(rt->source_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->source_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); - color_effect->set_texture("Diffuse", rt->source_texture[next_index]); + color_effect->set_texture("Diffuse", rt->source_texture[next_index].Get()); return next_index; } @@ -1112,13 +1144,23 @@ int shaders::color_convolution_pass(d3d_render_target *rt, int source_index, pol { int next_index = source_index; - curr_effect = color_effect; - curr_effect->update_uniforms(); + set_curr_effect(color_effect.get()); - // initial "Diffuse" texture is set in shaders::set_texture() or the result of shaders::ntsc_pass() + // initial "Diffuse" texture is set in shaders::ntsc_pass() if NTSC processing is enabled + if (!options->yiq_enable) + { + curr_effect->set_texture("Diffuse", diffuse_texture->get_finaltex()); + } + + curr_effect->update_uniforms(); + uint32_t tint = (uint32_t)poly->tint(); + float prim_tint[3] = { ((tint >> 16) & 0xff) / 255.0f, ((tint >> 8) & 0xff) / 255.0f, (tint & 0xff) / 255.0f }; + curr_effect->set_vector("PrimTint", 3, prim_tint); + curr_effect->set_texture("LutTexture", !lut_texture ? nullptr : lut_texture->get_finaltex()); + curr_effect->set_bool("UiLutEnable", false); next_index = rt->next_index(next_index); - blit(rt->source_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->source_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1127,12 +1169,12 @@ int shaders::prescale_pass(d3d_render_target *rt, int source_index, poly_info *p { int next_index = source_index; - curr_effect = prescale_effect; + set_curr_effect(filter_screens ? prescale_effect.get() : prescale_point_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->source_texture[next_index]); + curr_effect->set_texture("Diffuse", rt->source_texture[next_index].Get()); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1150,12 +1192,12 @@ int shaders::deconverge_pass(d3d_render_target *rt, int source_index, poly_info return next_index; } - curr_effect = deconverge_effect; + set_curr_effect(deconverge_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1165,12 +1207,11 @@ int shaders::scanline_pass(d3d_render_target *rt, int source_index, poly_info *p int next_index = source_index; // skip scanline if alpha is 0 - if (options->scanline_alpha == 0.0f) - return next_index; + //if (options->scanline_alpha == 0.0f) + //return next_index; - auto win = d3d->assert_window(); screen_device_enumerator screen_iterator(machine->root_device()); - screen_device *screen = screen_iterator.byindex(curr_screen); + screen_device *screen = screen_iterator.byindex(target_to_screen[curr_target]); render_container &screen_container = screen->container(); float xscale = 1.0f / screen_container.xscale(); float yscale = 1.0f / screen_container.yscale(); @@ -1179,16 +1220,16 @@ int shaders::scanline_pass(d3d_render_target *rt, int source_index, poly_info *p float screen_scale[] = { xscale, yscale }; float screen_offset[] = { xoffset, yoffset }; - curr_effect = scanline_effect; + set_curr_effect(scanline_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); curr_effect->set_vector("ScreenScale", 2, screen_scale); curr_effect->set_vector("ScreenOffset", 2, screen_offset); curr_effect->set_float("ScanlineOffset", curr_texture->get_cur_frame() == 0 ? 0.0f : options->scanline_jitter); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1202,12 +1243,12 @@ int shaders::defocus_pass(d3d_render_target *rt, int source_index, poly_info *po return next_index; } - curr_effect = focus_effect; + set_curr_effect(focus_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1223,23 +1264,23 @@ int shaders::phosphor_pass(d3d_render_target *rt, int source_index, poly_info *p } // Shader needs time between last update - curr_effect = phosphor_effect; + set_curr_effect(phosphor_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); - curr_effect->set_texture("LastPass", rt->cache_texture); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); + curr_effect->set_texture("LastPass", rt->cache_texture.Get()); curr_effect->set_bool("Passthrough", false); curr_effect->set_float("DeltaTime", delta_time()); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); // Pass along our phosphor'd screen curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); - curr_effect->set_texture("LastPass", rt->target_texture[next_index]); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); + curr_effect->set_texture("LastPass", rt->target_texture[next_index].Get()); curr_effect->set_bool("Passthrough", true); - blit(rt->cache_surface, false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->cache_surface.Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1248,10 +1289,8 @@ int shaders::post_pass(d3d_render_target *rt, int source_index, poly_info *poly, { int next_index = source_index; - auto win = d3d->assert_window(); - screen_device_enumerator screen_iterator(machine->root_device()); - screen_device *screen = screen_iterator.byindex(curr_screen); + screen_device *screen = screen_iterator.byindex(target_to_screen[curr_target]); render_container &screen_container = screen->container(); float xscale = 1.0f / screen_container.xscale(); @@ -1270,11 +1309,11 @@ int shaders::post_pass(d3d_render_target *rt, int source_index, poly_info *poly, float(back_color_rgb.g()) / 255.0f, float(back_color_rgb.b()) / 255.0f }; - curr_effect = post_effect; + set_curr_effect(post_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("ShadowTexture", shadow_texture == nullptr ? nullptr : shadow_texture->get_finaltex()); + curr_effect->set_texture("ShadowTexture", !shadow_texture ? nullptr : shadow_texture->get_finaltex()); curr_effect->set_int("ShadowTileMode", options->shadow_mask_tile_mode); - curr_effect->set_texture("DiffuseTexture", rt->target_texture[next_index]); + curr_effect->set_texture("DiffuseTexture", rt->target_texture[next_index].Get()); curr_effect->set_vector("BackColor", 3, back_color); curr_effect->set_vector("ScreenScale", 2, screen_scale); curr_effect->set_vector("ScreenOffset", 2, screen_offset); @@ -1283,7 +1322,7 @@ int shaders::post_pass(d3d_render_target *rt, int source_index, poly_info *poly, curr_effect->set_bool("PrepareBloom", prepare_bloom); next_index = rt->next_index(next_index); - blit(prepare_bloom ? rt->source_surface[next_index] : rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(prepare_bloom ? rt->source_surface[next_index].Get() : rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1292,11 +1331,11 @@ int shaders::chroma_pass(d3d_render_target *rt, int source_index, poly_info *pol { int next_index = source_index; - curr_effect = chroma_effect; + set_curr_effect(chroma_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1310,18 +1349,19 @@ int shaders::downsample_pass(d3d_render_target *rt, int source_index, poly_info return next_index; } - curr_effect = downsample_effect; - curr_effect->update_uniforms(); + set_curr_effect(downsample_effect.get()); for (int bloom_index = 0; bloom_index < rt->bloom_count; bloom_index++) { + curr_effect->update_uniforms(); curr_effect->set_vector("TargetDims", 2, rt->bloom_dims[bloom_index]); curr_effect->set_texture("DiffuseTexture", bloom_index == 0 - ? rt->source_texture[next_index] - : rt->bloom_texture[bloom_index - 1]); + ? rt->source_texture[next_index].Get() + : rt->bloom_texture[bloom_index - 1].Get()); - blit(rt->bloom_surface[bloom_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->bloom_surface[bloom_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); + downsample_effect->end(); } return next_index; @@ -1337,7 +1377,7 @@ int shaders::bloom_pass(d3d_render_target *rt, int source_index, poly_info *poly return next_index; } - curr_effect = bloom_effect; + set_curr_effect(bloom_effect.get()); curr_effect->update_uniforms(); curr_effect->set_float("Level0Weight", options->bloom_level0_weight); @@ -1354,22 +1394,22 @@ int shaders::bloom_pass(d3d_render_target *rt, int source_index, poly_info *poly curr_effect->set_float("BloomScale", options->bloom_scale); curr_effect->set_vector("BloomOverdrive", 3, options->bloom_overdrive); - curr_effect->set_texture("DiffuseTexture", rt->target_texture[next_index]); + curr_effect->set_texture("DiffuseTexture", rt->target_texture[next_index].Get()); char name[14] = "BloomTexture*"; for (int index = 1; index < rt->bloom_count; index++) { name[12] = 'A' + index - 1; - curr_effect->set_texture(name, rt->bloom_texture[index - 1]); + curr_effect->set_texture(name, rt->bloom_texture[index - 1].Get()); } for (int index = rt->bloom_count; index < MAX_BLOOM_COUNT; index++) { name[12] = 'A' + index - 1; - curr_effect->set_texture(name, black_texture); + curr_effect->set_texture(name, black_texture.Get()); } next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1390,12 +1430,12 @@ int shaders::distortion_pass(d3d_render_target *rt, int source_index, poly_info return next_index; } - curr_effect = distortion_effect; + set_curr_effect(distortion_effect.get()); curr_effect->update_uniforms(); - curr_effect->set_texture("DiffuseTexture", rt->target_texture[next_index]); + curr_effect->set_texture("DiffuseTexture", rt->target_texture[next_index].Get()); next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], false, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), false, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1404,14 +1444,14 @@ int shaders::vector_pass(d3d_render_target *rt, int source_index, poly_info *pol { int next_index = source_index; - curr_effect = vector_effect; + set_curr_effect(vector_effect.get()); curr_effect->update_uniforms(); curr_effect->set_float("LengthRatio", options->vector_length_ratio); curr_effect->set_float("LengthScale", options->vector_length_scale); curr_effect->set_float("BeamSmooth", options->vector_beam_smooth); // we need to clear the vector render target here - blit(rt->target_surface[next_index], true, poly->type(), vertnum, poly->count()); + blit(rt->target_surface[next_index].Get(), true, poly->type(), vertnum, poly->count()); return next_index; } @@ -1420,17 +1460,17 @@ int shaders::vector_buffer_pass(d3d_render_target *rt, int source_index, poly_in { int next_index = source_index; - curr_effect = default_effect; + set_curr_effect(vector_buffer_effect.get()); + curr_effect->set_texture("Diffuse", diffuse_texture->get_finaltex()); curr_effect->update_uniforms(); - curr_effect->set_technique("VectorBufferTechnique"); - - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); - curr_effect->set_texture("LutTexture", lut_texture == nullptr ? nullptr : lut_texture->get_finaltex()); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); + curr_effect->set_texture("LutTexture", !lut_texture ? nullptr : lut_texture->get_finaltex()); + curr_effect->set_bool("UiLutEnable", false); // we need to clear the vector render target here next_index = rt->next_index(next_index); - blit(rt->target_surface[next_index], true, D3DPT_TRIANGLELIST, 0, 2); + blit(rt->target_surface[next_index].Get(), true, D3DPT_TRIANGLELIST, 0, 2); return next_index; } @@ -1441,14 +1481,16 @@ int shaders::screen_pass(d3d_render_target *rt, int source_index, poly_info *pol d3d->set_blendmode(PRIMFLAG_GET_BLENDMODE(poly->flags())); - curr_effect = default_effect; + set_curr_effect(default_effect.get()); + curr_effect->set_texture("Diffuse", diffuse_texture->get_finaltex()); curr_effect->update_uniforms(); - curr_effect->set_technique("ScreenTechnique"); - curr_effect->set_texture("Diffuse", rt->target_texture[next_index]); - curr_effect->set_texture("LutTexture", lut_texture == nullptr ? nullptr : lut_texture->get_finaltex()); + curr_effect->set_texture("Diffuse", rt->target_texture[next_index].Get()); + curr_effect->set_texture("LutTexture", nullptr); + curr_effect->set_bool("LutEnable", false); + curr_effect->set_bool("UiLutEnable", false); - blit(backbuffer, false, poly->type(), vertnum, poly->count()); + blit(backbuffer.Get(), false, poly->type(), vertnum, poly->count()); if (recording_movie) { @@ -1460,9 +1502,9 @@ int shaders::screen_pass(d3d_render_target *rt, int source_index, poly_info *pol if (render_snap) { // we need to clear the snap render target here - blit(snap_target, true, poly->type(), vertnum, poly->count()); + blit(snap_target.Get(), true, poly->type(), vertnum, poly->count()); - render_snapshot(snap_target); + render_snapshot(snap_target.Get()); render_snap = false; } @@ -1474,11 +1516,11 @@ void shaders::ui_pass(poly_info *poly, int vertnum) { d3d->set_blendmode(PRIMFLAG_GET_BLENDMODE(poly->flags())); - curr_effect = default_effect; + set_curr_effect(PRIMFLAG_GET_TEXWRAP(poly->flags()) ? ui_wrap_effect.get() : ui_effect.get()); + curr_effect->set_texture("Diffuse", diffuse_texture->get_finaltex()); curr_effect->update_uniforms(); - curr_effect->set_technique("UiTechnique"); - curr_effect->set_texture("LutTexture", lut_texture == nullptr ? nullptr : ui_lut_texture->get_finaltex()); + curr_effect->set_texture("LutTexture", !lut_texture ? nullptr : ui_lut_texture->get_finaltex()); blit(nullptr, false, poly->type(), vertnum, poly->count()); } @@ -1498,8 +1540,6 @@ void shaders::render_quad(poly_info *poly, int vertnum) curr_texture = poly->texture(); curr_poly = poly; - auto win = d3d->assert_window(); - if (PRIMFLAG_GET_SCREENTEX(poly->flags())) { if (curr_texture == nullptr) @@ -1508,9 +1548,9 @@ void shaders::render_quad(poly_info *poly, int vertnum) return; } - curr_screen = curr_screen < num_screens ? curr_screen : 0; + curr_target = curr_target < num_targets ? curr_target : 0; - curr_render_target = find_render_target(curr_texture->get_width(), curr_texture->get_height(), curr_screen); + curr_render_target = find_render_target(curr_texture->get_width(), curr_texture->get_height(), curr_target); d3d_render_target *rt = curr_render_target; if (rt == nullptr) @@ -1530,12 +1570,16 @@ void shaders::render_quad(poly_info *poly, int vertnum) //next_index = phosphor_pass(rt, next_index, poly, vertnum); // create bloom textures - int old_index = next_index; - next_index = post_pass(rt, next_index, poly, vertnum, true); - next_index = downsample_pass(rt, next_index, poly, vertnum); + bool bloom_enabled = (options->bloom_scale > 0.0f); + if (bloom_enabled) + { + int old_index = next_index; + next_index = post_pass(rt, next_index, poly, vertnum, true); + next_index = downsample_pass(rt, next_index, poly, vertnum); + next_index = old_index; + } - // apply bloom textures - next_index = old_index; + // apply bloom textures (if enabled) and other post effects next_index = post_pass(rt, next_index, poly, vertnum, false); next_index = bloom_pass(rt, next_index, poly, vertnum); next_index = phosphor_pass(rt, next_index, poly, vertnum); @@ -1551,19 +1595,19 @@ void shaders::render_quad(poly_info *poly, int vertnum) curr_texture->increment_frame_count(); curr_texture->mask_frame_count(options->yiq_phase_count); - curr_screen++; + curr_target++; } else if (PRIMFLAG_GET_VECTOR(poly->flags())) { - curr_screen = curr_screen < num_screens ? curr_screen : 0; + curr_target = curr_target < num_targets ? curr_target : 0; int source_width = int(poly->prim_width() + 0.5f); int source_height = int(poly->prim_height() + 0.5f); - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(source_width, source_height); } - curr_render_target = find_render_target(source_width, source_height, curr_screen); + curr_render_target = find_render_target(source_width, source_height, curr_target); d3d_render_target *rt = curr_render_target; if (rt == nullptr) @@ -1576,25 +1620,25 @@ void shaders::render_quad(poly_info *poly, int vertnum) next_index = vector_pass(rt, next_index, poly, vertnum); - HRESULT result = d3d->get_device()->SetRenderTarget(0, backbuffer); + HRESULT result = d3d->get_device()->SetRenderTarget(0, backbuffer.Get()); if (FAILED(result)) { osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call\n", result); } - curr_screen++; + curr_target++; } else if (PRIMFLAG_GET_VECTORBUF(poly->flags())) { - curr_screen = curr_screen < num_screens ? curr_screen : 0; + curr_target = curr_target < num_targets ? curr_target : 0; int source_width = int(poly->prim_width() + 0.5f); int source_height = int(poly->prim_height() + 0.5f); - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(source_width, source_height); } - curr_render_target = find_render_target(source_width, source_height, curr_screen); + curr_render_target = find_render_target(source_width, source_height, curr_target); d3d_render_target *rt = curr_render_target; if (rt == nullptr) @@ -1629,7 +1673,7 @@ void shaders::render_quad(poly_info *poly, int vertnum) next_index = screen_pass(rt, next_index, poly, vertnum); d3d->set_wrap(PRIMFLAG_GET_TEXWRAP(curr_texture->get_flags()) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); - curr_screen++; + curr_target++; } else { @@ -1655,7 +1699,7 @@ void shaders::end_draw() return; } - backbuffer->Release(); + backbuffer->Release(); // TODO: where's the matching AddRef that requires this explicit Release? } @@ -1670,8 +1714,6 @@ d3d_render_target* shaders::get_texture_target(render_primitive *prim, int width return nullptr; } - auto win = d3d->assert_window(); - int source_width = width; int source_height = height; int source_screen = screen; @@ -1679,7 +1721,7 @@ d3d_render_target* shaders::get_texture_target(render_primitive *prim, int width int target_height = int(prim->get_full_quad_height() + 0.5f); target_width *= oversampling_enable ? 2 : 1; target_height *= oversampling_enable ? 2 : 1; - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(target_width, target_height); } @@ -1710,8 +1752,6 @@ d3d_render_target* shaders::get_vector_target(render_primitive *prim, int screen return nullptr; } - auto win = d3d->assert_window(); - int source_width = int(prim->get_quad_width() + 0.5f); int source_height = int(prim->get_quad_height() + 0.5f); int source_screen = screen; @@ -1719,7 +1759,7 @@ d3d_render_target* shaders::get_vector_target(render_primitive *prim, int screen int target_height = int(prim->get_full_quad_height() + 0.5f); target_width *= oversampling_enable ? 2 : 1; target_height *= oversampling_enable ? 2 : 1; - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(source_width, source_height); std::swap(target_width, target_height); @@ -1751,8 +1791,6 @@ bool shaders::create_vector_target(render_primitive *prim, int screen) return false; } - auto win = d3d->assert_window(); - int source_width = int(prim->get_quad_width() + 0.5f); int source_height = int(prim->get_quad_height() + 0.5f); int source_screen = screen; @@ -1760,7 +1798,7 @@ bool shaders::create_vector_target(render_primitive *prim, int screen) int target_height = int(prim->get_full_quad_height() + 0.5f); target_width *= oversampling_enable ? 2 : 1; target_height *= oversampling_enable ? 2 : 1; - if (win->swap_xy()) + if (d3d->window().swap_xy()) { std::swap(source_width, source_height); std::swap(target_width, target_height); @@ -1796,16 +1834,6 @@ bool shaders::add_render_target(renderer_d3d9* d3d, render_primitive *prim, int //============================================================ -// shaders::enumerate_screens -//============================================================ -void shaders::enumerate_screens() -{ - screen_device_enumerator iter(machine->root_device()); - num_screens = iter.count(); -} - - -//============================================================ // shaders::create_texture_target //============================================================ @@ -1816,8 +1844,6 @@ bool shaders::create_texture_target(render_primitive *prim, int width, int heigh return false; } - auto win = d3d->assert_window(); - int source_width = width; int source_height = height; int source_screen = screen; @@ -1825,7 +1851,7 @@ bool shaders::create_texture_target(render_primitive *prim, int width, int heigh int target_height = int(prim->get_full_quad_height() + 0.5f); target_width *= oversampling_enable ? 2 : 1; target_height *= oversampling_enable ? 2 : 1; - if (win->swap_xy()) + if (d3d->window().swap_xy()) { // source texture is already swapped std::swap(target_width, target_height); @@ -1863,93 +1889,29 @@ void shaders::delete_resources() m_render_target_list.clear(); - if (downsample_effect != nullptr) - { - delete downsample_effect; - downsample_effect = nullptr; - } - if (bloom_effect != nullptr) - { - delete bloom_effect; - bloom_effect = nullptr; - } - if (vector_effect != nullptr) - { - delete vector_effect; - vector_effect = nullptr; - } - if (default_effect != nullptr) - { - delete default_effect; - default_effect = nullptr; - } - if (post_effect != nullptr) - { - delete post_effect; - post_effect = nullptr; - } - if (distortion_effect != nullptr) - { - delete distortion_effect; - distortion_effect = nullptr; - } - if (prescale_effect != nullptr) - { - delete prescale_effect; - prescale_effect = nullptr; - } - if (phosphor_effect != nullptr) - { - delete phosphor_effect; - phosphor_effect = nullptr; - } - if (focus_effect != nullptr) - { - delete focus_effect; - focus_effect = nullptr; - } - if (scanline_effect != nullptr) - { - delete scanline_effect; - scanline_effect = nullptr; - } - if (deconverge_effect != nullptr) - { - delete deconverge_effect; - deconverge_effect = nullptr; - } - if (color_effect != nullptr) - { - delete color_effect; - color_effect = nullptr; - } - if (ntsc_effect != nullptr) - { - delete ntsc_effect; - ntsc_effect = nullptr; - } - if (chroma_effect != nullptr) - { - delete chroma_effect; - chroma_effect = nullptr; - } - - if (backbuffer != nullptr) - { - backbuffer->Release(); - backbuffer = nullptr; - } - - if (black_surface != nullptr) - { - black_surface->Release(); - black_surface = nullptr; - } - if (black_texture != nullptr) - { - black_texture->Release(); - black_texture = nullptr; - } + downsample_effect.reset(); + bloom_effect.reset(); + vector_effect.reset(); + default_effect.reset(); + ui_effect.reset(); + ui_wrap_effect.reset(); + vector_buffer_effect.reset(); + post_effect.reset(); + distortion_effect.reset(); + prescale_effect.reset(); + prescale_point_effect.reset(); + phosphor_effect.reset(); + focus_effect.reset(); + scanline_effect.reset(); + deconverge_effect.reset(); + color_effect.reset(); + ntsc_effect.reset(); + chroma_effect.reset(); + + backbuffer.Reset(); + + black_surface.Reset(); + black_texture.Reset(); shadow_bitmap.reset(); lut_bitmap.reset(); @@ -2215,13 +2177,13 @@ slider_desc shaders::s_sliders[] = { "NTSC Frame Jitter Offset", 0, 0, 100, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_JITTER, 0.01f, "%1.2f", {} }, { "NTSC A Value", -100, 50, 100, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_A_VALUE, 0.01f, "%1.2f", {} }, { "NTSC B Value", -100, 50, 100, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_B_VALUE, 0.01f, "%1.2f", {} }, - { "NTSC Incoming Phase Pixel Clock Scale",-300, 100, 300, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_P_VALUE, 0.01f, "%1.2f", {} }, - { "NTSC Outgoing Phase Offset", -300, 0, 300, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_O_VALUE, 0.01f, "%1.2f", {} }, - { "NTSC Color Carrier (Hz)", 0, 35795, 60000, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_CC_VALUE, 0.001f, "%1.4f", {} }, - { "NTSC Color Notch Filter Width", 0, 100, 600, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_N_VALUE, 0.01f, "%1.4f", {} }, - { "NTSC Y Signal Bandwidth (Hz)", 0, 600, 600, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_Y_VALUE, 0.01f, "%1.4f", {} }, - { "NTSC I Signal Bandwidth (Hz)", 0, 120, 600, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_I_VALUE, 0.01f, "%1.4f", {} }, - { "NTSC Q Signal Bandwidth (Hz)", 0, 60, 600, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_Q_VALUE, 0.01f, "%1.4f", {} }, + { "NTSC Incoming Phase Pixel Clock Scale",-300,100, 300, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_P_VALUE, 0.01f, "%1.2f", {} }, + { "NTSC Outgoing Phase Offset (radians)",-314, 0, 314, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_O_VALUE, 0.01f, "%1.2f", {} }, + { "NTSC Color Carrier (MHz)", 0, 31500,6*8800, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_CC_VALUE, 1/8800.f, "%1.5f", {} }, + { "NTSC Color Notch Filter Width (MHz)",0, 100, 600, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_N_VALUE, 0.01f, "%1.2f", {} }, + { "NTSC Y Signal Bandwidth (MHz)", 0, 600, 2100, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_Y_VALUE, 0.01f, "%1.2f", {} }, + { "NTSC I Signal Bandwidth (MHz)", 0, 120, 2100, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_I_VALUE, 0.01f, "%1.2f", {} }, + { "NTSC Q Signal Bandwidth (MHz)", 0, 60, 2100, 5, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_Q_VALUE, 0.01f, "%1.2f", {} }, { "NTSC Scanline Duration (uSec)", 0, 5260, 10000, 1, SLIDER_FLOAT, SLIDER_SCREEN_TYPE_LCD_OR_RASTER, SLIDER_NTSC_SCAN_TIME, 0.01f, "%1.2f", {} }, { "3D LUT (Screen)", 0, 0, 1, 1, SLIDER_INT_ENUM, SLIDER_SCREEN_TYPE_ANY, SLIDER_LUT_ENABLE, 0, "%s", { "Off", "On" } }, { nullptr, 0, 0, 0, 0, 0, 0, -1, 0, nullptr, {} } @@ -2313,10 +2275,6 @@ void shaders::init_slider_list() m_sliders.clear(); m_core_sliders.clear(); - for (slider* slider : internal_sliders) - { - delete slider; - } internal_sliders.clear(); const screen_device *first_screen = screen_device_enumerator(machine->root_device()).first(); @@ -2349,28 +2307,27 @@ void shaders::init_slider_list() for (int j = 0; j < count; j++) { - auto* slider_arg = new slider(desc, get_slider_option(desc->id, j), &options->params_dirty); - internal_sliders.push_back(slider_arg); + slider &slider_arg = *internal_sliders.emplace_back(std::make_unique<slider>(desc, get_slider_option(desc->id, j), &options->params_dirty)); std::string name = desc->name; switch (desc->slider_type) { case SLIDER_VEC2: { - std::string names[2] = { " X", " Y" }; - name = name + names[j]; + char const *const names[2] = { " X", " Y" }; + name += names[j]; break; } case SLIDER_COLOR: { - std::string names[3] = { " Red", " Green", " Blue" }; - name = name + names[j]; + char const *const names[3] = { " Red", " Green", " Blue" }; + name += names[j]; break; } default: break; } - std::unique_ptr<slider_state> core_slider = slider_alloc(std::move(name), desc->minval, desc->defval, desc->maxval, desc->step, slider_arg); + std::unique_ptr<slider_state> core_slider = slider_alloc(std::move(name), desc->minval, desc->defval, desc->maxval, desc->step, &slider_arg); ui::menu_item item(ui::menu_item_type::SLIDER, core_slider.get()); item.set_text(core_slider->description); @@ -2405,8 +2362,7 @@ void uniform::update() hlsl_options *options = shadersys->options; renderer_d3d9 *d3d = shadersys->d3d; - auto win = d3d->assert_window(); - const screen_device *first_screen = screen_device_enumerator(win->machine().root_device()).first(); + const screen_device *first_screen = screen_device_enumerator(d3d->window().machine().root_device()).first(); bool vector_screen = first_screen != nullptr && @@ -2422,7 +2378,7 @@ void uniform::update() } case CU_SCREEN_COUNT: { - int screen_count = win->target()->current_view().visible_screen_count(); + int screen_count = d3d->window().target()->current_view().visible_screen_count(); m_shader->set_int("ScreenCount", screen_count); break; } @@ -2485,7 +2441,7 @@ void uniform::update() case CU_SWAP_XY: { - m_shader->set_bool("SwapXY", win->swap_xy()); + m_shader->set_bool("SwapXY", d3d->window().swap_xy()); break; } case CU_VECTOR_SCREEN: @@ -2609,7 +2565,7 @@ void uniform::update() m_shader->set_float("SmoothBorderAmount", options->smooth_border); break; case CU_POST_SHADOW_ALPHA: - m_shader->set_float("ShadowAlpha", shadersys->shadow_texture == nullptr ? 0.0f : options->shadow_mask_alpha); + m_shader->set_float("ShadowAlpha", !shadersys->shadow_texture ? 0.0f : options->shadow_mask_alpha); break; case CU_POST_SHADOW_COUNT: { @@ -2684,12 +2640,14 @@ void uniform::update() //============================================================ effect::effect(shaders *shadersys, IDirect3DDevice9 *dev, const char *name, const char *path) + : m_effect(nullptr) + , m_shaders(shadersys) + , m_num_passes(0) + , m_valid(false) + , m_active(false) { LPD3DXBUFFER buffer_errors = nullptr; - m_shaders = shadersys; - m_valid = false; - char name_cstr[1024]; sprintf(name_cstr, "%s\\%s", path, name); auto effect_name = osd::text::to_tstring(name_cstr); @@ -2716,6 +2674,12 @@ effect::effect(shaders *shadersys, IDirect3DDevice9 *dev, const char *name, cons effect::~effect() { m_effect->Release(); + + m_vecs.clear(); + m_floats.clear(); + m_ints.clear(); + m_bools.clear(); + m_textures.clear(); } void effect::add_uniform(const char *name, uniform::uniform_type type, int id) @@ -2729,24 +2693,19 @@ void effect::update_uniforms() (*uniform).update(); } -void effect::begin(UINT *passes, DWORD flags) +void effect::begin(DWORD flags) { - m_effect->Begin(passes, flags); + m_active = true; + m_num_passes = 0; + m_effect->Begin(&m_num_passes, flags); + m_effect->BeginPass(0); } void effect::end() { - m_effect->End(); -} - -void effect::begin_pass(UINT pass) -{ - m_effect->BeginPass(pass); -} - -void effect::end_pass() -{ + m_active = false; m_effect->EndPass(); + m_effect->End(); } void effect::set_technique(const char *name) @@ -2756,48 +2715,97 @@ void effect::set_technique(const char *name) void effect::set_vector(D3DXHANDLE param, int count, float *vector) { - static D3DXVECTOR4 out_vector; - if (count > 0) - { - out_vector.x = vector[0]; - } - if (count > 1) - { - out_vector.y = vector[1]; - } - if (count > 2) + count = std::min(count, 4); + + auto iter = m_vecs.find(param); + if (iter != m_vecs.end()) { - out_vector.z = vector[2]; + D3DXVECTOR4 &data = iter->second; + if (!memcmp((float *)data, vector, sizeof(float) * count)) + { + return; + } + memcpy(data, vector, sizeof(float) * count); + m_effect->SetVector(param, &iter->second); } - if (count > 3) + else { - out_vector.w = vector[3]; + D3DXVECTOR4 vec; + memcpy((float *)vec, vector, sizeof(float) * count); + m_effect->SetVector(param, &vec); + m_vecs[param] = vec; } - m_effect->SetVector(param, &out_vector); } void effect::set_float(D3DXHANDLE param, float value) { + auto iter = m_floats.find(param); + if (iter != m_floats.end()) + { + if (iter->second == value) + { + return; + } + iter->second = value; + } + else + { + m_floats[param] = value; + } m_effect->SetFloat(param, value); } void effect::set_int(D3DXHANDLE param, int value) { + auto iter = m_ints.find(param); + if (iter != m_ints.end()) + { + if (iter->second == value) + { + return; + } + iter->second = value; + } + else + { + m_ints[param] = value; + } m_effect->SetInt(param, value); } void effect::set_bool(D3DXHANDLE param, bool value) { + auto iter = m_bools.find(param); + if (iter != m_bools.end()) + { + if (iter->second == value) + { + return; + } + iter->second = value; + } + else + { + m_bools[param] = value; + } m_effect->SetBool(param, value); } -void effect::set_matrix(D3DXHANDLE param, D3DXMATRIX *matrix) -{ - m_effect->SetMatrix(param, matrix); -} - void effect::set_texture(D3DXHANDLE param, IDirect3DTexture9 *tex) { + auto iter = m_textures.find(param); + if (iter != m_textures.end()) + { + if (iter->second == (void *)tex) + { + return; + } + iter->second = (void *)tex; + } + else + { + m_textures[param] = (void *)tex; + } m_effect->SetTexture(param, tex); } diff --git a/src/osd/modules/render/d3d/d3dhlsl.h b/src/osd/modules/render/d3d/d3dhlsl.h index f72197cb7b1..f06deca2a57 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.h +++ b/src/osd/modules/render/d3d/d3dhlsl.h @@ -9,11 +9,16 @@ #ifndef __WIN_D3DHLSL__ #define __WIN_D3DHLSL__ -#include <vector> #include "../frontend/mame/ui/menuitem.h" #include "../frontend/mame/ui/slider.h" #include "modules/lib/osdlib.h" +#include <wrl/client.h> + +#include <map> +#include <memory> +#include <vector> + //============================================================ // TYPE DEFINITIONS //============================================================ @@ -133,11 +138,8 @@ public: effect(shaders *shadersys, IDirect3DDevice9 *dev, const char *name, const char *path); ~effect(); - void begin(UINT *passes, DWORD flags); - void begin_pass(UINT pass); - + void begin(DWORD flags); void end(); - void end_pass(); void set_technique(const char *name); @@ -145,7 +147,6 @@ public: void set_float(D3DXHANDLE param, float value); void set_int(D3DXHANDLE param, int value); void set_bool(D3DXHANDLE param, bool value); - void set_matrix(D3DXHANDLE param, D3DXMATRIX *matrix); void set_texture(D3DXHANDLE param, IDirect3DTexture9 *tex); void add_uniform(const char *name, uniform::uniform_type type, int id); @@ -154,16 +155,26 @@ public: D3DXHANDLE get_parameter(D3DXHANDLE param, const char *name); shaders* get_shaders() { return m_shaders; } + uint32_t num_passes() { return m_num_passes; } bool is_valid() { return m_valid; } + bool is_active() { return m_active; } private: std::vector<std::unique_ptr<uniform>> m_uniform_list; ID3DXEffect *m_effect; shaders *m_shaders; + uint32_t m_num_passes; + + std::map<D3DXHANDLE, D3DXVECTOR4> m_vecs; + std::map<D3DXHANDLE, float> m_floats; + std::map<D3DXHANDLE, int> m_ints; + std::map<D3DXHANDLE, bool> m_bools; + std::map<D3DXHANDLE, void*> m_textures; bool m_valid; + bool m_active; }; class d3d_render_target; @@ -300,11 +311,14 @@ public: shaders(); ~shaders(); - bool init(d3d_base *d3dintf, running_machine *machine, renderer_d3d9 *renderer); + bool init(IDirect3D9 *d3dobj, running_machine *machine, renderer_d3d9 *renderer); - bool enabled() { return post_fx_enable && d3dintf->post_fx_available; } + bool enabled() { return post_fx_enable && d3d->post_fx_available(); } void toggle() { post_fx_enable = initialized && !post_fx_enable; } + void begin_frame(render_primitive_list *primlist); + void end_frame(); + void begin_draw(); void end_draw(); @@ -323,6 +337,7 @@ public: void init_fsfx_quad(); void set_texture(texture_info *info); + void set_filter(bool filter_screens); void remove_render_target(int source_width, int source_height, uint32_t screen_index); void remove_render_target(d3d_render_target *rt); @@ -336,8 +351,12 @@ public: void *get_slider_option(int id, int index = 0); private: + using IDirect3D9Ptr = Microsoft::WRL::ComPtr<IDirect3D9>; + using IDirect3DTexture9Ptr = Microsoft::WRL::ComPtr<IDirect3DTexture9>; + using IDirect3DSurface9Ptr = Microsoft::WRL::ComPtr<IDirect3DSurface9>; + + void set_curr_effect(effect *curr_effect); void blit(IDirect3DSurface9 *dst, bool clear_dst, D3DPRIMITIVETYPE prim_type, uint32_t prim_index, uint32_t prim_count); - void enumerate_screens(); void render_snapshot(IDirect3DSurface9 *surface); // Time since last call, only updates once per render of all screens @@ -364,7 +383,7 @@ private: int screen_pass(d3d_render_target *rt, int source_index, poly_info *poly, int vertnum); void ui_pass(poly_info *poly, int vertnum); - d3d_base * d3dintf; // D3D interface + IDirect3D9Ptr d3dobj; // D3D interface running_machine * machine; renderer_d3d9 * d3d; // D3D renderer @@ -372,7 +391,10 @@ private: bool post_fx_enable; // overall enable flag bool oversampling_enable; // oversampling enable flag int num_screens; // number of emulated physical screens - int curr_screen; // current screen for render target operations + int num_targets; // number of emulated screen targets (can be different from above; cf. artwork and Laserdisc games) + int curr_target; // current target index for render target operations + int targets_per_screen[256]; // screen target count per screen device/container index; estimated maximum count for array size + int target_to_screen[256]; // lookup from target index to screen device/container index; estimated maximum count for array size double acc_t; // accumulated machine time double delta_t; // data for delta_time bitmap_argb32 shadow_bitmap; // shadow mask bitmap for post-processing shader @@ -383,49 +405,55 @@ private: texture_info * ui_lut_texture; hlsl_options * options; // current options - IDirect3DSurface9 * black_surface; // black dummy surface - IDirect3DTexture9 * black_texture; // black dummy texture + IDirect3DSurface9Ptr black_surface; // black dummy surface + IDirect3DTexture9Ptr black_texture; // black dummy texture bool recording_movie; // ongoing movie recording std::unique_ptr<movie_recorder> recorder; // HLSL post-render movie recorder bool render_snap; // whether or not to take HLSL post-render snapshot - IDirect3DSurface9 * snap_copy_target; // snapshot destination surface in system memory - IDirect3DTexture9 * snap_copy_texture; // snapshot destination surface in system memory - IDirect3DSurface9 * snap_target; // snapshot upscaled surface - IDirect3DTexture9 * snap_texture; // snapshot upscaled texture + IDirect3DSurface9Ptr snap_copy_target; // snapshot destination surface in system memory + IDirect3DTexture9Ptr snap_copy_texture; // snapshot destination surface in system memory + IDirect3DSurface9Ptr snap_target; // snapshot upscaled surface + IDirect3DTexture9Ptr snap_texture; // snapshot upscaled texture int snap_width; // snapshot width int snap_height; // snapshot height bool initialized; // whether or not we're initialized // HLSL effects - IDirect3DSurface9 * backbuffer; // pointer to our device's backbuffer + IDirect3DSurface9Ptr backbuffer; // pointer to our device's backbuffer effect * curr_effect; // pointer to the currently active effect object - effect * default_effect; // pointer to the primary-effect object - effect * prescale_effect; // pointer to the prescale-effect object - effect * post_effect; // pointer to the post-effect object - effect * distortion_effect; // pointer to the distortion-effect object - effect * scanline_effect; - effect * focus_effect; // pointer to the focus-effect object - effect * phosphor_effect; // pointer to the phosphor-effect object - effect * deconverge_effect; // pointer to the deconvergence-effect object - effect * color_effect; // pointer to the color-effect object - effect * ntsc_effect; // pointer to the NTSC effect object - effect * bloom_effect; // pointer to the bloom composite effect - effect * downsample_effect; // pointer to the bloom downsample effect - effect * vector_effect; // pointer to the vector-effect object - effect * chroma_effect; - + std::unique_ptr<effect> default_effect; // pointer to the primary-effect object + std::unique_ptr<effect> ui_effect; // pointer to the UI-element effect object + std::unique_ptr<effect> ui_wrap_effect; // pointer to the UI-element effect object with texture wrapping + std::unique_ptr<effect> vector_buffer_effect; // pointer to the vector-buffering effect object + std::unique_ptr<effect> prescale_effect; // pointer to the prescale-effect object + std::unique_ptr<effect> prescale_point_effect; // pointer to the prescale-effect object with point filtering + std::unique_ptr<effect> post_effect; // pointer to the post-effect object + std::unique_ptr<effect> distortion_effect; // pointer to the distortion-effect object + std::unique_ptr<effect> scanline_effect; + std::unique_ptr<effect> focus_effect; // pointer to the focus-effect object + std::unique_ptr<effect> phosphor_effect; // pointer to the phosphor-effect object + std::unique_ptr<effect> deconverge_effect; // pointer to the deconvergence-effect object + std::unique_ptr<effect> color_effect; // pointer to the color-effect object + std::unique_ptr<effect> ntsc_effect; // pointer to the NTSC effect object + std::unique_ptr<effect> bloom_effect; // pointer to the bloom composite effect + std::unique_ptr<effect> downsample_effect; // pointer to the bloom downsample effect + std::unique_ptr<effect> vector_effect; // pointer to the vector-effect object + std::unique_ptr<effect> chroma_effect; + + texture_info * diffuse_texture; + bool filter_screens; texture_info * curr_texture; d3d_render_target * curr_render_target; poly_info * curr_poly; std::vector<std::unique_ptr<d3d_render_target>> m_render_target_list; - std::vector<slider*> internal_sliders; + std::vector<std::unique_ptr<slider> > internal_sliders; std::vector<ui::menu_item> m_sliders; - std::vector<std::unique_ptr<slider_state>> m_core_sliders; + std::vector<std::unique_ptr<slider_state> > m_core_sliders; static slider_desc s_sliders[]; static hlsl_options last_options; // last used options diff --git a/src/osd/modules/render/draw13.cpp b/src/osd/modules/render/draw13.cpp index af7c8559ca0..a36ee8a8f52 100644 --- a/src/osd/modules/render/draw13.cpp +++ b/src/osd/modules/render/draw13.cpp @@ -2,7 +2,7 @@ // copyright-holders: Couriersud, Olivier Galibert, R. Belmont //============================================================ // -// draw13.c - SDL 2.0 drawing implementation +// draw13.cpp - SDL 2.0 drawing implementation // // SDLMAME by Olivier Galibert and R. Belmont // @@ -10,24 +10,202 @@ // //============================================================ +#include "render_module.h" + +#include "modules/osdmodule.h" + +#if defined(OSD_SDL) + +// OSD headers +#include "sdlopts.h" +#include "window.h" + +// lib/util +#include "options.h" + +// emu +#include "emucore.h" +#include "render.h" + +// standard SDL headers +#include <SDL2/SDL.h> + // standard C headers +#include <algorithm> #include <cmath> #include <cstdio> +#include <iterator> +#include <list> -// MAME headers -#include "emu.h" -#include "options.h" -// OSD headers -#include "osdsdl.h" -#include "window.h" +namespace osd { + +namespace { + +struct quad_setup_data +{ + quad_setup_data() = default; + + void compute(const render_primitive &prim, const int prescale); + + int32_t dudx = 0, dvdx = 0, dudy = 0, dvdy = 0; + int32_t startu = 0, startv = 0; + int32_t rotwidth = 0, rotheight = 0; +}; + +//============================================================ +// Textures +//============================================================ + +class renderer_sdl2; +struct copy_info_t; + +/* texture_info holds information about a texture */ +class texture_info +{ +public: + texture_info(renderer_sdl2 *renderer, const render_texinfo &texsource, const quad_setup_data &setup, const uint32_t flags); + ~texture_info(); + + void set_data(const render_texinfo &texsource, const uint32_t flags); + void render_quad(const render_primitive &prim, const int x, const int y); + bool matches(const render_primitive &prim, const quad_setup_data &setup); + + copy_info_t const *compute_size_type(); + + void *m_pixels; // pixels for the texture + int m_pitch; + + copy_info_t const *m_copyinfo; + quad_setup_data m_setup; + + osd_ticks_t m_last_access; + + int raw_width() const { return m_texinfo.width; } + int raw_height() const { return m_texinfo.height; } + + const render_texinfo &texinfo() const { return m_texinfo; } + render_texinfo &texinfo() { return m_texinfo; } + + HashT hash() const { return m_hash; } + uint32_t flags() const { return m_flags; } + +private: + bool is_pixels_owned() const; + + void set_coloralphamode(SDL_Texture *texture_id, const render_color *color); + + Uint32 m_sdl_access; + renderer_sdl2 * m_renderer; + render_texinfo m_texinfo; // copy of the texture info + HashT m_hash; // hash value for the texture (must be >= pointer size) + uint32_t m_flags; // rendering flags -#include "draw13.h" + SDL_Texture * m_texture_id; + bool m_is_rotated; + + int m_format; // texture format + SDL_BlendMode m_sdl_blendmode; +}; + +// inline functions and macros +#include "blit13.ipp" //============================================================ -// DEBUGGING +// TEXCOPY FUNCS //============================================================ +enum SDL_TEXFORMAT_E +{ + SDL_TEXFORMAT_ARGB32 = 0, + SDL_TEXFORMAT_RGB32, + SDL_TEXFORMAT_RGB32_PALETTED, + SDL_TEXFORMAT_YUY16, + SDL_TEXFORMAT_YUY16_PALETTED, + SDL_TEXFORMAT_PALETTE16, + SDL_TEXFORMAT_RGB15, + SDL_TEXFORMAT_RGB15_PALETTED, + SDL_TEXFORMAT_PALETTE16A, + SDL_TEXFORMAT_PALETTE16_ARGB1555, + SDL_TEXFORMAT_RGB15_ARGB1555, + SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555, + SDL_TEXFORMAT_LAST = SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555 +}; + +struct copy_info_t +{ + int src_fmt; + Uint32 dst_fmt; + const blit_base *blitter; + Uint32 bm_mask; + const char *srcname; + const char *dstname; + /* Statistics */ + mutable uint64_t pixel_count; + mutable int64_t time; + mutable int samples; + mutable int perf; + /* list */ + copy_info_t const *next; +}; + +/* renderer_sdl2 is the information about SDL for the current screen */ +class renderer_sdl2 : public osd_renderer +{ +public: + renderer_sdl2( + osd_window &window, + copy_info_t const *const (&blit_info)[SDL_TEXFORMAT_LAST + 1]); + + virtual ~renderer_sdl2() + { + destroy_all_textures(); + SDL_DestroyRenderer(m_sdl_renderer); + m_sdl_renderer = nullptr; + } + + virtual int create() override; + virtual int draw(const int update) override; + virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; + virtual render_primitive_list *get_primitives() override; + + int RendererSupportsFormat(Uint32 format, Uint32 access, const char *sformat); + + SDL_Renderer *m_sdl_renderer; + copy_info_t const *const (&m_blit_info)[SDL_TEXFORMAT_LAST + 1]; + +private: + void render_quad(texture_info *texture, const render_primitive &prim, const int x, const int y); + + texture_info *texture_find(const render_primitive &prim, const quad_setup_data &setup); + texture_info *texture_update(const render_primitive &prim); + + void destroy_all_textures(); + + int32_t m_blittimer; + + std::list<texture_info> m_texlist; // list of active textures + + float m_last_hofs; + float m_last_vofs; + + int m_width; + int m_height; + + osd_dim m_blit_dim; + + struct + { + Uint32 format; + int status; + } fmt_support[30]; + + // Stats + int64_t m_last_blit_time; + int64_t m_last_blit_pixels; +}; + + //============================================================ // CONSTANTS //============================================================ @@ -60,9 +238,12 @@ static inline bool is_transparent(const float &a) // CONSTRUCTOR & DESTRUCTOR //============================================================ -renderer_sdl2::renderer_sdl2(std::shared_ptr<osd_window> window, int extra_flags) - : osd_renderer(window, FLAG_NEEDS_OPENGL | extra_flags) +renderer_sdl2::renderer_sdl2( + osd_window &window, + copy_info_t const *const (&blit_info)[SDL_TEXFORMAT_LAST + 1]) + : osd_renderer(window) , m_sdl_renderer(nullptr) + , m_blit_info(blit_info) , m_blittimer(0) , m_last_hofs(0) , m_last_vofs(0) @@ -77,112 +258,8 @@ renderer_sdl2::renderer_sdl2(std::shared_ptr<osd_window> window, int extra_flags fmt_support[i].format = 0; fmt_support[i].status = 0; } - - if (!s_blit_info_initialized) - { - /* On OSX, calling this from drawsdl2_init will - * prohibit fullscreen toggling. It is than not possible - * to toggle from fullscreen to window mode. - */ - expand_copy_info(s_blit_info_default); - s_blit_info_initialized = true; - } } - -//============================================================ -// STATIC VARIABLES -//============================================================ - -#define BM_ALL (UINT32_MAX) -//( SDL_BLENDMODE_MASK | SDL_BLENDMODE_BLEND | SDL_BLENDMODE_ADD | SDL_BLENDMODE_MOD) - -#define ENTRY(a,b,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, 0} -#define ENTRY_BM(a,b,f,bm) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, bm, #a, #b, 0, 0, 0, 0} -#define ENTRY_LR(a,b,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, -1} - -const copy_info_t renderer_sdl2::s_blit_info_default[] = -{ - /* no rotation */ - ENTRY(ARGB32, ARGB8888, argb32_argb32), - ENTRY_LR(ARGB32, RGB888, argb32_rgb32), - /* Entry primarily for directfb */ - ENTRY_BM(ARGB32, RGB888, argb32_rgb32, SDL_BLENDMODE_ADD), - ENTRY_BM(ARGB32, RGB888, argb32_rgb32, SDL_BLENDMODE_MOD), - ENTRY_BM(ARGB32, RGB888, argb32_rgb32, SDL_BLENDMODE_NONE), - - ENTRY(RGB32, ARGB8888, rgb32_argb32), - ENTRY(RGB32, RGB888, rgb32_rgb32), - - ENTRY(RGB32_PALETTED, ARGB8888, rgb32pal_argb32), - ENTRY(RGB32_PALETTED, RGB888, rgb32pal_argb32), - - ENTRY(YUY16, UYVY, yuv16_uyvy), - ENTRY(YUY16, YUY2, yuv16_yuy2), - ENTRY(YUY16, YVYU, yuv16_yvyu), - ENTRY(YUY16, ARGB8888, yuv16_argb32), - ENTRY(YUY16, RGB888, yuv16_argb32), - - ENTRY(YUY16_PALETTED, UYVY, yuv16pal_uyvy), - ENTRY(YUY16_PALETTED, YUY2, yuv16pal_yuy2), - ENTRY(YUY16_PALETTED, YVYU, yuv16pal_yvyu), - ENTRY(YUY16_PALETTED, ARGB8888, yuv16pal_argb32), - ENTRY(YUY16_PALETTED, RGB888, yuv16pal_argb32), - - ENTRY(PALETTE16, ARGB8888, pal16_argb32), - ENTRY(PALETTE16, RGB888, pal16_argb32), - - ENTRY(RGB15, RGB555, rgb15_rgb555), - ENTRY(RGB15, ARGB1555, rgb15_argb1555), - ENTRY(RGB15, ARGB8888, rgb15_argb32), - ENTRY(RGB15, RGB888, rgb15_argb32), - - ENTRY(RGB15_PALETTED, ARGB8888, rgb15pal_argb32), - ENTRY(RGB15_PALETTED, RGB888, rgb15pal_argb32), - - ENTRY(PALETTE16A, ARGB8888, pal16a_argb32), - ENTRY(PALETTE16A, RGB888, pal16a_rgb32), - - /* rotation */ - ENTRY(ARGB32, ARGB8888, rot_argb32_argb32), - ENTRY_LR(ARGB32, RGB888, rot_argb32_rgb32), - /* Entry primarily for directfb */ - ENTRY_BM(ARGB32, RGB888, rot_argb32_rgb32, SDL_BLENDMODE_ADD), - ENTRY_BM(ARGB32, RGB888, rot_argb32_rgb32, SDL_BLENDMODE_MOD), - ENTRY_BM(ARGB32, RGB888, rot_argb32_rgb32, SDL_BLENDMODE_NONE), - - ENTRY(RGB32, ARGB8888, rot_rgb32_argb32), - ENTRY(RGB32, RGB888, rot_argb32_argb32), - - ENTRY(RGB32_PALETTED, ARGB8888, rot_rgb32pal_argb32), - ENTRY(RGB32_PALETTED, RGB888, rot_rgb32pal_argb32), - - ENTRY(YUY16, ARGB8888, rot_yuv16_argb32rot), - ENTRY(YUY16, RGB888, rot_yuv16_argb32rot), - - ENTRY(YUY16_PALETTED, ARGB8888, rot_yuv16pal_argb32rot), - ENTRY(YUY16_PALETTED, RGB888, rot_yuv16pal_argb32rot), - - ENTRY(PALETTE16, ARGB8888, rot_pal16_argb32), - ENTRY(PALETTE16, RGB888, rot_pal16_argb32), - - ENTRY(RGB15, RGB555, rot_rgb15_argb1555), - ENTRY(RGB15, ARGB1555, rot_rgb15_argb1555), - ENTRY(RGB15, ARGB8888, rot_rgb15_argb32), - ENTRY(RGB15, RGB888, rot_rgb15_argb32), - - ENTRY(RGB15_PALETTED, ARGB8888, rot_rgb15pal_argb32), - ENTRY(RGB15_PALETTED, RGB888, rot_rgb15pal_argb32), - - ENTRY(PALETTE16A, ARGB8888, rot_pal16a_argb32), - ENTRY(PALETTE16A, RGB888, rot_pal16a_rgb32), - -{ -1 }, -}; - -copy_info_t* renderer_sdl2::s_blit_info[SDL_TEXFORMAT_LAST+1] = { nullptr }; -bool renderer_sdl2::s_blit_info_initialized = false; - //============================================================ // INLINES //============================================================ @@ -277,7 +354,7 @@ void renderer_sdl2::render_quad(texture_info *texture, const render_primitive &p if (texture) { - copy_info_t *copyinfo = texture->m_copyinfo; + copy_info_t const *copyinfo = texture->m_copyinfo; copyinfo->time -= osd_ticks(); texture->render_quad(prim, x, y); copyinfo->time += osd_ticks(); @@ -288,7 +365,7 @@ void renderer_sdl2::render_quad(texture_info *texture, const render_primitive &p copyinfo->time += (m_last_blit_time * (int64_t) (texture->raw_width() * texture->raw_height())) / (int64_t) m_last_blit_pixels; } copyinfo->samples++; - copyinfo->perf = ( texture->m_copyinfo->pixel_count * (osd_ticks_per_second()/1000)) / texture->m_copyinfo->time; + copyinfo->perf = (texture->m_copyinfo->pixel_count * (osd_ticks_per_second()/1000)) / std::max<int64_t>(texture->m_copyinfo->time, 1); } else { @@ -329,59 +406,6 @@ int renderer_sdl2::RendererSupportsFormat(Uint32 format, Uint32 access, const ch return 0; } -//============================================================ -// drawsdl_init -//============================================================ - -void renderer_sdl2::add_list(copy_info_t **head, const copy_info_t *element, Uint32 bm) -{ - copy_info_t *newci = new copy_info_t; - *newci = *element; - - newci->bm_mask = bm; - newci->next = *head; - *head = newci; -} - -void renderer_sdl2::expand_copy_info(const copy_info_t *list) -{ - for (const copy_info_t *bi = list; bi->src_fmt != -1; bi++) - { - if (bi->bm_mask == BM_ALL) - { - add_list(&s_blit_info[bi->src_fmt], bi, SDL_BLENDMODE_NONE); - add_list(&s_blit_info[bi->src_fmt], bi, SDL_BLENDMODE_ADD); - add_list(&s_blit_info[bi->src_fmt], bi, SDL_BLENDMODE_MOD); - add_list(&s_blit_info[bi->src_fmt], bi, SDL_BLENDMODE_BLEND); - } - else - { - add_list(&s_blit_info[bi->src_fmt], bi, bi->bm_mask); - } - } -} - -// FIXME: machine only used to access options. -void renderer_sdl2::init(running_machine &machine) -{ - osd_printf_verbose("Using SDL native texturing driver (SDL 2.0+)\n"); - -#if USE_OPENGL - // Load the GL library now - else MT will fail - const char *stemp = downcast<sdl_options &>(machine.options()).gl_lib(); -#else - const char *stemp = nullptr; -#endif - if (stemp != nullptr && strcmp(stemp, OSDOPTVAL_AUTO) == 0) - stemp = nullptr; - - // No fatalerror here since not all video drivers support GL ! - if (SDL_GL_LoadLibrary(stemp) != 0) // Load library (default for e==nullptr - osd_printf_warning("Warning: Unable to load opengl library: %s\n", stemp ? stemp : "<default>"); - else - osd_printf_verbose("Loaded opengl shared library: %s\n", stemp ? stemp : "<default>"); -} - //============================================================ // sdl_info::create @@ -434,12 +458,10 @@ int renderer_sdl2::create() SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); } - auto win = assert_window(); - if (video_config.waitvsync) - m_sdl_renderer = SDL_CreateRenderer(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); else - m_sdl_renderer = SDL_CreateRenderer(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window(), -1, SDL_RENDERER_ACCELERATED); + m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_ACCELERATED); if (!m_sdl_renderer) { @@ -483,18 +505,14 @@ int renderer_sdl2::xy_to_render_target(int x, int y, int *xt, int *yt) void renderer_sdl2::destroy_all_textures() { - auto win = assert_window(); - if (win == nullptr) - return; - - if(win->m_primlist) + if (window().m_primlist) { - win->m_primlist->acquire_lock(); - m_texlist.reset(); - win->m_primlist->release_lock(); + window().m_primlist->acquire_lock(); + m_texlist.clear(); + window().m_primlist->release_lock(); } else - m_texlist.reset(); + m_texlist.clear(); } //============================================================ @@ -507,13 +525,7 @@ int renderer_sdl2::draw(int update) float vofs, hofs; int blit_pixels = 0; - if (video_config.novideo) - { - return 0; - } - - auto win = assert_window(); - osd_dim wdim = win->get_size(); + osd_dim wdim = window().get_size(); if (has_flags(FI_CHANGED) || (wdim.width() != m_width) || (wdim.height() != m_height)) { @@ -560,10 +572,10 @@ int renderer_sdl2::draw(int update) m_last_hofs = hofs; m_last_vofs = vofs; - win->m_primlist->acquire_lock(); + window().m_primlist->acquire_lock(); // now draw - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { Uint8 sr, sg, sb, sa; @@ -593,7 +605,7 @@ int renderer_sdl2::draw(int update) } } - win->m_primlist->release_lock(); + window().m_primlist->release_lock(); m_last_blit_pixels = blit_pixels; m_last_blit_time = -osd_ticks(); @@ -612,22 +624,23 @@ int renderer_sdl2::draw(int update) // texture_compute_size and type //============================================================ -copy_info_t *texture_info::compute_size_type() +copy_info_t const *texture_info::compute_size_type() { - copy_info_t *result = nullptr; + copy_info_t const *result = nullptr; int maxperf = 0; - for (copy_info_t *bi = renderer_sdl2::s_blit_info[m_format]; bi != nullptr; bi = bi->next) + for (copy_info_t const *bi = m_renderer->m_blit_info[m_format]; bi != nullptr; bi = bi->next) { - if ((m_is_rotated == bi->blitter->m_is_rot) - && (m_sdl_blendmode == bi->bm_mask)) + if ((m_is_rotated == bi->blitter->m_is_rot) && (m_sdl_blendmode == bi->bm_mask)) { if (m_renderer->RendererSupportsFormat(bi->dst_fmt, m_sdl_access, bi->dstname)) { - int perf = bi->perf; + int const perf = bi->perf; if (perf == 0) + { return bi; - else if (perf > (maxperf * 102) / 100) + } + else if (perf > ((maxperf * 102) / 100)) { result = bi; maxperf = perf; @@ -639,23 +652,21 @@ copy_info_t *texture_info::compute_size_type() if (result) return result; - /* try last resort handlers */ - for (copy_info_t *bi = renderer_sdl2::s_blit_info[m_format]; bi != nullptr; bi = bi->next) + // try last resort handlers + for (copy_info_t const *bi = m_renderer->m_blit_info[m_format]; bi != nullptr; bi = bi->next) { - if ((m_is_rotated == bi->blitter->m_is_rot) - && (m_sdl_blendmode == bi->bm_mask)) + if ((m_is_rotated == bi->blitter->m_is_rot) && (m_sdl_blendmode == bi->bm_mask)) if (m_renderer->RendererSupportsFormat(bi->dst_fmt, m_sdl_access, bi->dstname)) return bi; } - //FIXME: crash implement a -do nothing handler */ + //FIXME: crash implement a -do nothing handler return nullptr; } -// FIXME: bool texture_info::is_pixels_owned() const -{ // do we own / allocated it ? - return ((m_sdl_access == SDL_TEXTUREACCESS_STATIC) - && (m_copyinfo->blitter->m_is_passthrough)); +{ + // do we own / allocated it ? + return (m_sdl_access == SDL_TEXTUREACCESS_STATIC) && !m_copyinfo->blitter->m_is_passthrough; } //============================================================ @@ -745,12 +756,11 @@ texture_info::texture_info(renderer_sdl2 *renderer, const render_texinfo &texsou m_pixels = malloc(m_setup.rotwidth * m_setup.rotheight * m_copyinfo->blitter->m_dest_bpp); } m_last_access = osd_ticks(); - } texture_info::~texture_info() { - if ( is_pixels_owned() && (m_pixels != nullptr) ) + if (is_pixels_owned() && m_pixels) free(m_pixels); SDL_DestroyTexture(m_texture_id); } @@ -764,7 +774,7 @@ void texture_info::set_data(const render_texinfo &texsource, const uint32_t flag m_copyinfo->time -= osd_ticks(); if (m_sdl_access == SDL_TEXTUREACCESS_STATIC) { - if ( m_copyinfo->blitter->m_is_passthrough ) + if (m_copyinfo->blitter->m_is_passthrough) { m_pixels = texsource.base; m_pitch = m_texinfo.rowpixels * m_copyinfo->blitter->m_dest_bpp; @@ -778,11 +788,11 @@ void texture_info::set_data(const render_texinfo &texsource, const uint32_t flag } else { - SDL_LockTexture(m_texture_id, nullptr, (void **) &m_pixels, &m_pitch); + SDL_LockTexture(m_texture_id, nullptr, (void **)&m_pixels, &m_pitch); if ( m_copyinfo->blitter->m_is_passthrough ) { - uint8_t *src = (uint8_t *) texsource.base; - uint8_t *dst = (uint8_t *) m_pixels; + const uint8_t *src = (uint8_t *)texsource.base; + uint8_t *dst = (uint8_t *)m_pixels; int spitch = texsource.rowpixels * m_copyinfo->blitter->m_dest_bpp; int num = texsource.width * m_copyinfo->blitter->m_dest_bpp; int h = texsource.height; @@ -868,64 +878,38 @@ void quad_setup_data::compute(const render_primitive &prim, const int prescale) texture_info *renderer_sdl2::texture_find(const render_primitive &prim, const quad_setup_data &setup) { - HashT texhash = texture_compute_hash(prim.texture, prim.flags); - texture_info *texture; - osd_ticks_t now = osd_ticks(); + const HashT texhash = texture_compute_hash(prim.texture, prim.flags); + const osd_ticks_t now = osd_ticks(); // find a match - for (texture = m_texlist.first(); texture != nullptr; ) - if (texture->hash() == texhash && - texture->matches(prim, setup)) + for (auto texture = m_texlist.begin(); texture != m_texlist.end(); ) + { + if ((texture->hash() == texhash) && texture->matches(prim, setup)) { - /* would we choose another blitter based on performance ? */ + // would we choose another blitter based on performance? if ((texture->m_copyinfo->samples & 0x7f) == 0x7f) { if (texture->m_copyinfo != texture->compute_size_type()) return nullptr; } texture->m_last_access = now; - return texture; + return &*texture; } else { - /* free resources not needed any longer? */ - texture_info *expire = texture; - texture = texture->next(); - if (now - expire->m_last_access > osd_ticks_per_second()) - m_texlist.remove(*expire); + // free resources not needed any longer? + if ((now - texture->m_last_access) > osd_ticks_per_second()) + texture = m_texlist.erase(texture); + else + ++texture; } + } // nothing found return nullptr; } //============================================================ -// exit -//============================================================ - -void renderer_sdl2::exit() -{ - if (s_blit_info_initialized) - { - for (int i = 0; i <= SDL_TEXFORMAT_LAST; i++) - { - for (copy_info_t *bi = s_blit_info[i]; bi != nullptr; ) - { - if (bi->pixel_count) - osd_printf_verbose("%s -> %s %s blendmode 0x%02x, %d samples: %d KPixel/sec\n", bi->srcname, bi->dstname, - bi->blitter->m_is_rot ? "rot" : "norot", bi->bm_mask, bi->samples, - (int) bi->perf); - copy_info_t *freeme = bi; - bi = bi->next; - delete freeme; - } - s_blit_info[i] = nullptr; - } - s_blit_info_initialized = false; - } -} - -//============================================================ // texture_update //============================================================ @@ -934,22 +918,20 @@ texture_info * renderer_sdl2::texture_update(const render_primitive &prim) quad_setup_data setup; texture_info *texture; - auto win = assert_window(); - setup.compute(prim, win->prescale()); + setup.compute(prim, window().prescale()); texture = texture_find(prim, setup); // if we didn't find one, create a new texture - if (texture == nullptr && prim.texture.base != nullptr) + if (!texture && prim.texture.base) { - texture = new texture_info(this, prim.texture, setup, prim.flags); - /* add us to the texture list */ - m_texlist.prepend(*texture); + // add us to the texture list + texture = &m_texlist.emplace_front(this, prim.texture, setup, prim.flags); } - if (texture != nullptr) + if (texture) { - if (prim.texture.base != nullptr && texture->texinfo().seqid != prim.texture.seqid) + if (prim.texture.base && (texture->texinfo().seqid != prim.texture.seqid)) { texture->texinfo().seqid = prim.texture.seqid; // if we found it, but with a different seqid, copy the data @@ -962,16 +944,249 @@ texture_info * renderer_sdl2::texture_update(const render_primitive &prim) render_primitive_list *renderer_sdl2::get_primitives() { - auto win = assert_window(); - if (win == nullptr) - return nullptr; - - osd_dim nd = win->get_size(); + osd_dim nd = window().get_size(); if (nd != m_blit_dim) { m_blit_dim = nd; notify_changed(); } - win->target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), win->pixel_aspect()); - return &win->target()->get_primitives(); + window().target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), window().pixel_aspect()); + return &window().target()->get_primitives(); +} + + +class video_sdl2 : public osd_module, public render_module +{ +public: + video_sdl2() + : osd_module(OSD_RENDERER_PROVIDER, "accel") + , m_blit_info_initialized(false) + , m_gllib_loaded(false) + { + std::fill(std::begin(m_blit_info), std::end(m_blit_info), nullptr); + } + ~video_sdl2() + { + free_copy_info(); + } + + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override { free_copy_info(); } + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return FLAG_INTERACTIVE | FLAG_SDL_NEEDS_OPENGL; } + +private: + static inline constexpr Uint32 BM_ALL = UINT32_MAX; // SDL_BLENDMODE_MASK | SDL_BLENDMODE_BLEND | SDL_BLENDMODE_ADD | SDL_BLENDMODE_MOD + + void expand_copy_info(); + void free_copy_info(); + + static void add_list(copy_info_t const *&head, copy_info_t const &element, Uint32 bm); + + copy_info_t const *m_blit_info[SDL_TEXFORMAT_LAST + 1]; + bool m_blit_info_initialized; + bool m_gllib_loaded; + + static copy_info_t const s_blit_info_default[]; +}; + +int video_sdl2::init(osd_interface &osd, osd_options const &options) +{ + osd_printf_verbose("Using SDL native texturing driver (SDL 2.0+)\n"); + + // Load the GL library now - else MT will fail + char const *libname = nullptr; +#if USE_OPENGL + libname = dynamic_cast<sdl_options const &>(options).gl_lib(); + if (libname && (!*libname || !std::strcmp(libname, OSDOPTVAL_AUTO))) + libname = nullptr; +#endif + + if (!m_gllib_loaded) + { + // No fatalerror here since not all video drivers support GL! + if (SDL_GL_LoadLibrary(libname) != 0) + { + osd_printf_error("Unable to load OpenGL shared library: %s\n", libname ? libname : "<default>"); + m_gllib_loaded = true; + } + else + { + osd_printf_verbose("Loaded OpenGL shared library: %s\n", libname ? libname : "<default>"); + } + } + + return 0; +} + +std::unique_ptr<osd_renderer> video_sdl2::create(osd_window &window) +{ + if (!m_blit_info_initialized) + { + // On macOS, calling this from drawsdl2_init will prohibit fullscreen toggling. + // It is than not possible to toggle from fullscreen to window mode. + expand_copy_info(); + m_blit_info_initialized = true; + } + + return std::make_unique<renderer_sdl2>(window, m_blit_info); +} + +void video_sdl2::expand_copy_info() +{ + for (const copy_info_t *bi = s_blit_info_default; bi->src_fmt != -1; bi++) + { + if (bi->bm_mask == BM_ALL) + { + add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_NONE); + add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_ADD); + add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_MOD); + add_list(m_blit_info[bi->src_fmt], *bi, SDL_BLENDMODE_BLEND); + } + else + { + add_list(m_blit_info[bi->src_fmt], *bi, bi->bm_mask); + } + } +} + +void video_sdl2::free_copy_info() +{ + if (m_blit_info_initialized) + { + for (int i = 0; i <= SDL_TEXFORMAT_LAST; i++) + { + for (copy_info_t const *bi = m_blit_info[i]; bi != nullptr; ) + { + if (bi->pixel_count) + { + osd_printf_verbose( + "%s -> %s %s blendmode 0x%02x, %d samples: %d KPixel/sec\n", + bi->srcname, + bi->dstname, + bi->blitter->m_is_rot ? "rot" : "norot", + bi->bm_mask, + bi->samples, + bi->perf); + } + delete std::exchange(bi, bi->next); + } + m_blit_info[i] = nullptr; + } + m_blit_info_initialized = false; + } +} + +void video_sdl2::add_list(copy_info_t const *&head, copy_info_t const &element, Uint32 bm) +{ + copy_info_t *const newci = new copy_info_t(element); + + newci->bm_mask = bm; + newci->next = head; + head = newci; } + + +//============================================================ +// STATIC VARIABLES +//============================================================ + +#define ENTRY(a,b,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, 0} +#define ENTRY_BM(a,b,f,bm) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, bm, #a, #b, 0, 0, 0, 0} +#define ENTRY_LR(a,b,f) { SDL_TEXFORMAT_ ## a, SDL_PIXELFORMAT_ ## b, &texcopy_ ## f, BM_ALL, #a, #b, 0, 0, 0, -1} + +copy_info_t const video_sdl2::s_blit_info_default[] = +{ + /* no rotation */ + ENTRY(ARGB32, ARGB8888, argb32_argb32), + ENTRY_LR(ARGB32, RGB888, argb32_rgb32), + /* Entry primarily for directfb */ + ENTRY_BM(ARGB32, RGB888, argb32_rgb32, SDL_BLENDMODE_ADD), + ENTRY_BM(ARGB32, RGB888, argb32_rgb32, SDL_BLENDMODE_MOD), + ENTRY_BM(ARGB32, RGB888, argb32_rgb32, SDL_BLENDMODE_NONE), + + ENTRY(RGB32, ARGB8888, rgb32_argb32), + ENTRY(RGB32, RGB888, rgb32_rgb32), + + ENTRY(RGB32_PALETTED, ARGB8888, rgb32pal_argb32), + ENTRY(RGB32_PALETTED, RGB888, rgb32pal_argb32), + + ENTRY(YUY16, UYVY, yuv16_uyvy), + ENTRY(YUY16, YUY2, yuv16_yuy2), + ENTRY(YUY16, YVYU, yuv16_yvyu), + ENTRY(YUY16, ARGB8888, yuv16_argb32), + ENTRY(YUY16, RGB888, yuv16_argb32), + + ENTRY(YUY16_PALETTED, UYVY, yuv16pal_uyvy), + ENTRY(YUY16_PALETTED, YUY2, yuv16pal_yuy2), + ENTRY(YUY16_PALETTED, YVYU, yuv16pal_yvyu), + ENTRY(YUY16_PALETTED, ARGB8888, yuv16pal_argb32), + ENTRY(YUY16_PALETTED, RGB888, yuv16pal_argb32), + + ENTRY(PALETTE16, ARGB8888, pal16_argb32), + ENTRY(PALETTE16, RGB888, pal16_argb32), + + ENTRY(RGB15, RGB555, rgb15_rgb555), + ENTRY(RGB15, ARGB1555, rgb15_argb1555), + ENTRY(RGB15, ARGB8888, rgb15_argb32), + ENTRY(RGB15, RGB888, rgb15_argb32), + + ENTRY(RGB15_PALETTED, ARGB8888, rgb15pal_argb32), + ENTRY(RGB15_PALETTED, RGB888, rgb15pal_argb32), + + ENTRY(PALETTE16A, ARGB8888, pal16a_argb32), + ENTRY(PALETTE16A, RGB888, pal16a_rgb32), + + /* rotation */ + ENTRY(ARGB32, ARGB8888, rot_argb32_argb32), + ENTRY_LR(ARGB32, RGB888, rot_argb32_rgb32), + /* Entry primarily for directfb */ + ENTRY_BM(ARGB32, RGB888, rot_argb32_rgb32, SDL_BLENDMODE_ADD), + ENTRY_BM(ARGB32, RGB888, rot_argb32_rgb32, SDL_BLENDMODE_MOD), + ENTRY_BM(ARGB32, RGB888, rot_argb32_rgb32, SDL_BLENDMODE_NONE), + + ENTRY(RGB32, ARGB8888, rot_rgb32_argb32), + ENTRY(RGB32, RGB888, rot_argb32_argb32), + + ENTRY(RGB32_PALETTED, ARGB8888, rot_rgb32pal_argb32), + ENTRY(RGB32_PALETTED, RGB888, rot_rgb32pal_argb32), + + ENTRY(YUY16, ARGB8888, rot_yuv16_argb32rot), + ENTRY(YUY16, RGB888, rot_yuv16_argb32rot), + + ENTRY(YUY16_PALETTED, ARGB8888, rot_yuv16pal_argb32rot), + ENTRY(YUY16_PALETTED, RGB888, rot_yuv16pal_argb32rot), + + ENTRY(PALETTE16, ARGB8888, rot_pal16_argb32), + ENTRY(PALETTE16, RGB888, rot_pal16_argb32), + + ENTRY(RGB15, RGB555, rot_rgb15_argb1555), + ENTRY(RGB15, ARGB1555, rot_rgb15_argb1555), + ENTRY(RGB15, ARGB8888, rot_rgb15_argb32), + ENTRY(RGB15, RGB888, rot_rgb15_argb32), + + ENTRY(RGB15_PALETTED, ARGB8888, rot_rgb15pal_argb32), + ENTRY(RGB15_PALETTED, RGB888, rot_rgb15pal_argb32), + + ENTRY(PALETTE16A, ARGB8888, rot_pal16a_argb32), + ENTRY(PALETTE16A, RGB888, rot_pal16a_rgb32), + + { -1 }, +}; + +} // anonymous namespace + +} // namespace osd + + +#else // defined(OSD_SDL) + +namespace osd { namespace { MODULE_NOT_SUPPORTED(video_sdl2, OSD_RENDERER_PROVIDER, "accel") } } + +#endif // defined(OSD_SDL) + + +MODULE_DEFINITION(RENDERER_SDL2, osd::video_sdl2) diff --git a/src/osd/modules/render/draw13.h b/src/osd/modules/render/draw13.h deleted file mode 100644 index d69c84c1c9d..00000000000 --- a/src/osd/modules/render/draw13.h +++ /dev/null @@ -1,216 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Couriersud, Olivier Galibert, R. Belmont -//============================================================ -// -// draw13.h - SDL 2.0 drawing implementation -// -// SDLMAME by Olivier Galibert and R. Belmont -// -//============================================================ - -#ifndef MAME_OSD_MODULES_RENDER_DRAW13_H -#define MAME_OSD_MODULES_RENDER_DRAW13_H - -#pragma once - -// OSD headers -#ifndef OSD_WINDOWS -#include "osdsdl.h" -#include "window.h" -#else -#include "../windows/window.h" -typedef uint64_t HashT; -#endif - -// standard SDL headers -#include <SDL2/SDL.h> - -#include "emucore.h" -#include "render.h" - -struct quad_setup_data -{ - quad_setup_data() - : dudx(0) - , dvdx(0) - , dudy(0) - , dvdy(0) - , startu(0) - , startv(0) - , rotwidth(0) - , rotheight(0) - { - } - - void compute(const render_primitive &prim, const int prescale); - - int32_t dudx, dvdx, dudy, dvdy; - int32_t startu, startv; - int32_t rotwidth, rotheight; -}; - -//============================================================ -// Textures -//============================================================ - -class renderer_sdl2; -struct copy_info_t; - -/* texture_info holds information about a texture */ -class texture_info -{ - friend class simple_list<texture_info>; -public: - texture_info(renderer_sdl2 *renderer, const render_texinfo &texsource, const quad_setup_data &setup, const uint32_t flags); - ~texture_info(); - - void set_data(const render_texinfo &texsource, const uint32_t flags); - void render_quad(const render_primitive &prim, const int x, const int y); - bool matches(const render_primitive &prim, const quad_setup_data &setup); - - copy_info_t *compute_size_type(); - - void *m_pixels; // pixels for the texture - int m_pitch; - - copy_info_t *m_copyinfo; - quad_setup_data m_setup; - - osd_ticks_t m_last_access; - - int raw_width() const { return m_texinfo.width; } - int raw_height() const { return m_texinfo.height; } - - texture_info *next() { return m_next; } - const render_texinfo &texinfo() const { return m_texinfo; } - render_texinfo &texinfo() { return m_texinfo; } - - HashT hash() const { return m_hash; } - uint32_t flags() const { return m_flags; } - // FIXME: - bool is_pixels_owned() const; - -private: - void set_coloralphamode(SDL_Texture *texture_id, const render_color *color); - - Uint32 m_sdl_access; - renderer_sdl2 * m_renderer; - render_texinfo m_texinfo; // copy of the texture info - HashT m_hash; // hash value for the texture (must be >= pointer size) - uint32_t m_flags; // rendering flags - - SDL_Texture * m_texture_id; - bool m_is_rotated; - - int m_format; // texture format - SDL_BlendMode m_sdl_blendmode; - - texture_info * m_next; // next texture in the list -}; - -//============================================================ -// TEXCOPY FUNCS -//============================================================ - -enum SDL_TEXFORMAT_E -{ - SDL_TEXFORMAT_ARGB32 = 0, - SDL_TEXFORMAT_RGB32, - SDL_TEXFORMAT_RGB32_PALETTED, - SDL_TEXFORMAT_YUY16, - SDL_TEXFORMAT_YUY16_PALETTED, - SDL_TEXFORMAT_PALETTE16, - SDL_TEXFORMAT_RGB15, - SDL_TEXFORMAT_RGB15_PALETTED, - SDL_TEXFORMAT_PALETTE16A, - SDL_TEXFORMAT_PALETTE16_ARGB1555, - SDL_TEXFORMAT_RGB15_ARGB1555, - SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555, - SDL_TEXFORMAT_LAST = SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555 -}; - -#include "blit13.h" - -struct copy_info_t -{ - int src_fmt; - Uint32 dst_fmt; - const blit_base *blitter; - Uint32 bm_mask; - const char *srcname; - const char *dstname; - /* Statistics */ - uint64_t pixel_count; - int64_t time; - int samples; - int perf; - /* list */ - copy_info_t *next; -}; - -/* renderer_sdl2 is the information about SDL for the current screen */ -class renderer_sdl2 : public osd_renderer -{ -public: - renderer_sdl2(std::shared_ptr<osd_window> window, int extra_flags); - - virtual ~renderer_sdl2() - { - destroy_all_textures(); - SDL_DestroyRenderer(m_sdl_renderer); - m_sdl_renderer = nullptr; - } - - static void init(running_machine &machine); - static void exit(); - - virtual int create() override; - virtual int draw(const int update) override; - virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; - virtual render_primitive_list *get_primitives() override; - - int RendererSupportsFormat(Uint32 format, Uint32 access, const char *sformat); - - SDL_Renderer * m_sdl_renderer; - - static copy_info_t* s_blit_info[SDL_TEXFORMAT_LAST+1]; - -private: - void expand_copy_info(const copy_info_t *list); - void add_list(copy_info_t **head, const copy_info_t *element, Uint32 bm); - - void render_quad(texture_info *texture, const render_primitive &prim, const int x, const int y); - - texture_info *texture_find(const render_primitive &prim, const quad_setup_data &setup); - texture_info *texture_update(const render_primitive &prim); - - void destroy_all_textures(); - - int32_t m_blittimer; - - - simple_list<texture_info> m_texlist; // list of active textures - - float m_last_hofs; - float m_last_vofs; - - int m_width; - int m_height; - - osd_dim m_blit_dim; - - struct - { - Uint32 format; - int status; - } fmt_support[30]; - - // Stats - int64_t m_last_blit_time; - int64_t m_last_blit_pixels; - - static bool s_blit_info_initialized; - static const copy_info_t s_blit_info_default[]; -}; - -#endif // MAME_OSD_MODULES_RENDER_DRAW13_H diff --git a/src/osd/modules/render/drawbgfx.cpp b/src/osd/modules/render/drawbgfx.cpp index fc9214fe446..36758914961 100644 --- a/src/osd/modules/render/drawbgfx.cpp +++ b/src/osd/modules/render/drawbgfx.cpp @@ -5,6 +5,40 @@ // drawbgfx.cpp - BGFX renderer // //============================================================ + +#include "drawbgfx.h" + +// render/bgfx +#include "bgfx/effect.h" +#include "bgfx/effectmanager.h" +#include "bgfx/shadermanager.h" +#include "bgfx/slider.h" +#include "bgfx/target.h" +#include "bgfx/target.h" +#include "bgfx/targetmanager.h" +#include "bgfx/texture.h" +#include "bgfx/texturemanager.h" +#include "bgfx/uniform.h" +#include "bgfx/view.h" + +// render +#include "aviwrite.h" +#include "bgfxutil.h" +#include "render_module.h" + +// emu +#include "emu.h" +#include "config.h" +#include "render.h" +#include "rendutil.h" + +// util +#include "util/xmlfile.h" + +// OSD +#include "modules/lib/osdobj_common.h" +#include "window.h" + #include <bx/math.h> #include <bx/readerwriter.h> @@ -22,334 +56,658 @@ extern void *GetOSWindow(void *wincontroller); #endif #endif -// MAMEOS headers -#include "emu.h" -#include "window.h" -#include "render.h" -#include "rendutil.h" -#include "aviwrite.h" +#if defined(SDLMAME_USE_WAYLAND) +#include <wayland-egl.h> +#endif #include <bgfx/bgfx.h> #include <bgfx/platform.h> -#include <algorithm> -#include "drawbgfx.h" -#include "bgfxutil.h" -#include "bgfx/texturemanager.h" -#include "bgfx/targetmanager.h" -#include "bgfx/shadermanager.h" -#include "bgfx/effectmanager.h" -#include "bgfx/chainmanager.h" -#include "bgfx/effect.h" -#include "bgfx/texture.h" -#include "bgfx/target.h" -#include "bgfx/chain.h" -#include "bgfx/vertex.h" -#include "bgfx/uniform.h" -#include "bgfx/slider.h" -#include "bgfx/target.h" -#include "bgfx/view.h" +#include "imgui/imgui.h" -#include "modules/lib/osdobj_common.h" +#include <algorithm> -#include "imgui/imgui.h" //============================================================ -// DEBUGGING +// Renderer interface to parent module //============================================================ -//============================================================ -// CONSTANTS -//============================================================ +class renderer_bgfx::parent_module +{ +public: + util::xml::data_node &persistent_settings() { return *m_persistent_settings; } + osd_options const &options() const { return *m_options; } + uint32_t max_texture_size() const { return m_max_texture_size; } + + template <typename T> + util::notifier_subscription subscribe_load(void (T::*func)(util::xml::data_node const &), T *obj) + { + return m_load_notifier.subscribe(delegate<void (util::xml::data_node const &)>(func, obj)); + } + + template <typename T> + util::notifier_subscription subscribe_save(void (T::*func)(util::xml::data_node &), T *obj) + { + return m_save_notifier.subscribe(delegate<void (util::xml::data_node &)>(func, obj)); + } + +protected: + parent_module() + : m_options(nullptr) + , m_max_texture_size(0) + , m_renderer_count(0) + { + } + virtual ~parent_module() + { + assert(!m_renderer_count); + } + + bool has_active_renderers() const + { + return 0 < m_renderer_count; + } + + util::notifier<util::xml::data_node const &> m_load_notifier; + util::notifier<util::xml::data_node &> m_save_notifier; + util::xml::file::ptr m_persistent_settings; + osd_options const *m_options; + uint32_t m_max_texture_size; + +private: + friend class parent_module_holder; + + virtual void last_renderer_destroyed() = 0; + + void renderer_created() + { + ++m_renderer_count; + } + + void renderer_destroyed() + { + assert(m_renderer_count); + if (!--m_renderer_count) + last_renderer_destroyed(); + } + + unsigned m_renderer_count; +}; + + +inline renderer_bgfx::parent_module_holder::parent_module_holder(parent_module &parent) + : m_parent(parent) +{ + m_parent.renderer_created(); +} + + +inline renderer_bgfx::parent_module_holder::~parent_module_holder() +{ + m_parent.renderer_destroyed(); +} + -uint16_t const renderer_bgfx::CACHE_SIZE = 1024; -uint32_t const renderer_bgfx::PACKABLE_SIZE = 128; -uint32_t const renderer_bgfx::WHITE_HASH = 0x87654321; -char const *const renderer_bgfx::WINDOW_PREFIX = "Window 0, "; //============================================================ -// MACROS +// OSD MODULE //============================================================ -#define GIBBERISH (0) -#define SCENE_VIEW (0) +namespace osd { + +namespace { + +class video_bgfx : public osd_module, public render_module, protected renderer_bgfx::parent_module +{ +public: + video_bgfx() + : osd_module(OSD_RENDERER_PROVIDER, "bgfx") + , m_bgfx_library_initialized(false) + { + } + ~video_bgfx() { exit(); } + + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override; + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return FLAG_INTERACTIVE; } + +private: + virtual void last_renderer_destroyed() override; + + void load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode); + void save_config(config_type cfg_type, util::xml::data_node *parentnode); + + bool init_bgfx_library(osd_window &window); + + static bool set_platform_data(bgfx::PlatformData &platform_data, osd_window const &window); + + bool m_bgfx_library_initialized; +}; + //============================================================ -// STATICS +// video_bgfx::init //============================================================ -uint32_t renderer_bgfx::s_current_view = 0; -bool renderer_bgfx::s_bgfx_library_initialized = false; -uint32_t renderer_bgfx::s_width[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -uint32_t renderer_bgfx::s_height[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +int video_bgfx::init(osd_interface &osd, osd_options const &options) +{ + m_options = &options; + m_persistent_settings = util::xml::file::create(); + + // Check that BGFX directory exists + char const *const bgfx_path = options.bgfx_path(); + osd::directory::ptr directory = osd::directory::open(bgfx_path); + if (!directory) + { + osd_printf_error("Unable to find the BGFX path %s, please install it or fix the bgfx_path setting to use the BGFX renderer.\n", bgfx_path); + return -1; + } + directory.reset(); + + // Verify baseline shaders + const bool gui_opaque_valid = effect_manager::validate_effect(options, "gui_opaque"); + const bool gui_blend_valid = effect_manager::validate_effect(options, "gui_blend"); + const bool gui_multiply_valid = effect_manager::validate_effect(options, "gui_multiply"); + const bool gui_add_valid = effect_manager::validate_effect(options, "gui_add"); + const bool all_gui_valid = gui_opaque_valid && gui_blend_valid && gui_multiply_valid && gui_add_valid; + + const bool screen_opaque_valid = effect_manager::validate_effect(options, "screen_opaque"); + const bool screen_blend_valid = effect_manager::validate_effect(options, "screen_blend"); + const bool screen_multiply_valid = effect_manager::validate_effect(options, "screen_multiply"); + const bool screen_add_valid = effect_manager::validate_effect(options, "screen_add"); + const bool all_screen_valid = screen_opaque_valid && screen_blend_valid && screen_multiply_valid && screen_add_valid; + + if (!all_gui_valid || !all_screen_valid) + { + osd_printf_error("BGFX: Unable to load required shaders. Please update the %s folder or adjust your bgfx_path setting.\n", options.bgfx_path()); + return -1; + } + + m_max_texture_size = 16384; // Relatively safe default on modern GPUs + + // Register configuration handlers - do this last because it can't be undone + downcast<osd_common_t &>(osd).machine().configuration().config_register( + "bgfx", + configuration_manager::load_delegate(&video_bgfx::load_config, this), + configuration_manager::save_delegate(&video_bgfx::save_config, this)); + + return 0; +} + //============================================================ -// renderer_bgfx - constructor +// video_bgfx::exit //============================================================ -renderer_bgfx::renderer_bgfx(std::shared_ptr<osd_window> w) - : osd_renderer(w, FLAG_NONE) - , m_options(downcast<osd_options &>(w->machine().options())) - , m_framebuffer(nullptr) - , m_texture_cache(nullptr) - , m_dimensions(0, 0) - , m_textures(nullptr) - , m_targets(nullptr) - , m_shaders(nullptr) - , m_effects(nullptr) - , m_chains(nullptr) - , m_ortho_view(nullptr) - , m_max_view(0) - , m_avi_view(nullptr) - , m_avi_writer(nullptr) - , m_avi_target(nullptr) +void video_bgfx::exit() { + assert(!has_active_renderers()); + + if (m_bgfx_library_initialized) + { + osd_printf_verbose("Shutting down BGFX library\n"); + imguiDestroy(); + bgfx::shutdown(); + m_bgfx_library_initialized = false; + } + m_max_texture_size = 0; + m_persistent_settings.reset(); + m_options = nullptr; } + //============================================================ -// renderer_bgfx - destructor +// video_bgfx::create //============================================================ -renderer_bgfx::~renderer_bgfx() +std::unique_ptr<osd_renderer> video_bgfx::create(osd_window &window) { - bgfx::reset(0, 0, BGFX_RESET_NONE); - - if (m_avi_writer != nullptr && m_avi_writer->recording()) + // start BGFX if this is the first window + if (!m_bgfx_library_initialized) { - m_avi_writer->stop(); + assert(window.index() == 0); // bad things will happen otherwise + assert(!has_active_renderers()); - m_targets->destroy_target("avibuffer0"); - m_avi_target = nullptr; + osd_printf_verbose("Initializing BGFX library\n"); + if (!init_bgfx_library(window)) + { + osd_printf_error("BGFX library initialization failed\n"); + return nullptr; + } + m_bgfx_library_initialized = true; + } - bgfx::destroy(m_avi_texture); + return std::make_unique<renderer_bgfx>(window, static_cast<renderer_bgfx::parent_module &>(*this)); +} - delete m_avi_writer; - delete [] m_avi_data; - delete m_avi_view; + +//============================================================ +// video_bgfx::last_renderer_destroyed +//============================================================ + +void video_bgfx::last_renderer_destroyed() +{ + if (m_bgfx_library_initialized) + { + osd_printf_verbose("No more renderers - shutting down BGFX library\n"); + imguiDestroy(); + bgfx::shutdown(); + m_bgfx_library_initialized = false; + m_max_texture_size = 0; } +} - // Cleanup. - if (m_ortho_view) - delete m_ortho_view; - delete m_chains; - delete m_effects; - delete m_shaders; - delete m_textures; - delete m_targets; +//============================================================ +// video_bgfx::load_config +//============================================================ + +void video_bgfx::load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode) +{ + if ((cfg_type == config_type::SYSTEM) && parentnode) + m_load_notifier(*parentnode); } +//============================================================ +// video_bgfx::save_config +//============================================================ + +void video_bgfx::save_config(config_type cfg_type, util::xml::data_node *parentnode) +{ + if (cfg_type == config_type::SYSTEM) + m_save_notifier(*parentnode); +} + //============================================================ -// renderer_bgfx::init_bgfx_library +// video_bgfx::init_bgfx_library //============================================================ -void renderer_bgfx::init_bgfx_library() +bool video_bgfx::init_bgfx_library(osd_window &window) { - std::string backend(m_options.bgfx_backend()); + osd_dim const wdim = window.get_size(); bgfx::Init init; init.type = bgfx::RendererType::Count; init.vendorId = BGFX_PCI_ID_NONE; - init.resolution.width = s_width[0]; - init.resolution.height = s_height[0]; + init.resolution.width = wdim.width(); + init.resolution.height = wdim.height(); init.resolution.numBackBuffers = 1; - init.resolution.reset = BGFX_RESET_NONE; - init.platformData = m_platform_data; - if (backend == "auto") + init.resolution.reset = video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE; + if (!set_platform_data(init.platformData, window)) { + osd_printf_error("Setting BGFX platform data failed\n"); + return false; } + + std::string_view const backend(m_options->bgfx_backend()); + if (backend == "auto") + ; // do nothing else if (backend == "dx9" || backend == "d3d9") - { init.type = bgfx::RendererType::Direct3D9; - } else if (backend == "dx11" || backend == "d3d11") - { init.type = bgfx::RendererType::Direct3D11; - } else if (backend == "dx12" || backend == "d3d12") - { init.type = bgfx::RendererType::Direct3D12; - } else if (backend == "gles") - { init.type = bgfx::RendererType::OpenGLES; - } else if (backend == "glsl" || backend == "opengl") - { init.type = bgfx::RendererType::OpenGL; - } else if (backend == "vulkan") - { init.type = bgfx::RendererType::Vulkan; - } else if (backend == "metal") - { init.type = bgfx::RendererType::Metal; - } else - { - osd_printf_verbose("Unknown backend type '%s', going with auto-detection.\n", backend.c_str()); - } + osd_printf_warning("Unknown BGFX backend type '%s', going with auto-detection.\n", backend); - bgfx::init(init); - bgfx::reset(s_width[0], s_height[0], video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE); - // Enable debug text if requested. - bool bgfx_debug = m_options.bgfx_debug(); + if (!bgfx::init(init)) + return false; + + bgfx::reset(wdim.width(), wdim.height(), video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE); + + // Enable debug text if requested + bool bgfx_debug = m_options->bgfx_debug(); bgfx::setDebug(bgfx_debug ? BGFX_DEBUG_STATS : BGFX_DEBUG_TEXT); + // Get actual maximum texture size + bgfx::Caps const *const caps = bgfx::getCaps(); + m_max_texture_size = caps->limits.maxTextureSize; + ScreenVertex::init(); imguiCreate(); -} + return true; +} //============================================================ -// Utilities for setting up window handles +// Helper for creating a wayland window //============================================================ -#ifdef OSD_WINDOWS -inline void winSetHwnd(bgfx::PlatformData &platform_data, ::HWND _window) -{ - platform_data.ndt = NULL; - platform_data.nwh = _window; - platform_data.context = NULL; - platform_data.backBuffer = NULL; - platform_data.backBufferDS = NULL; - bgfx::setPlatformData(platform_data); -} -#elif defined(OSD_MAC) -inline void macSetWindow(bgfx::PlatformData &platform_data, void *_window) -{ - platform_data.ndt = NULL; - platform_data.nwh = GetOSWindow(_window); - platform_data.context = NULL; - platform_data.backBuffer = NULL; - platform_data.backBufferDS = NULL; - bgfx::setPlatformData(platform_data); -} -#elif defined(OSD_SDL) -static void* sdlNativeWindowHandle(SDL_Window* _window) +#if defined(SDLMAME_USE_WAYLAND) +wl_egl_window *create_wl_egl_window(SDL_Window *window, struct wl_surface *surface) { - SDL_SysWMinfo wmi; - SDL_VERSION(&wmi.version); - if (!SDL_GetWindowWMInfo(_window, &wmi)) + if (!surface) { + osd_printf_error("Wayland surface missing, aborting\n"); return nullptr; } - -# if BX_PLATFORM_LINUX || BX_PLATFORM_BSD || BX_PLATFORM_RPI - return (void*)wmi.info.x11.window; -# elif BX_PLATFORM_OSX - return wmi.info.cocoa.window; -# elif BX_PLATFORM_WINDOWS - return wmi.info.win.window; -# elif BX_PLATFORM_EMSCRIPTEN || BX_PLATFORM_ANDROID - return nullptr; -# endif // BX_PLATFORM_ + 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 -inline bool sdlSetWindow(bgfx::PlatformData &platform_data, SDL_Window* _window) + +//============================================================ +// Utility for setting up window handle +//============================================================ + +bool video_bgfx::set_platform_data(bgfx::PlatformData &platform_data, osd_window const &window) { +#if defined(OSD_WINDOWS) + platform_data.ndt = nullptr; + platform_data.nwh = dynamic_cast<win_window_info const &>(window).platform_window(); +#elif defined(OSD_MAC) + platform_data.ndt = nullptr; + platform_data.nwh = GetOSWindow(dynamic_cast<mac_window_info const &>(window).platform_window()); +#else // defined(OSD_*) SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); - if (!SDL_GetWindowWMInfo(_window, &wmi) ) + if (!SDL_GetWindowWMInfo(dynamic_cast<sdl_window_info const &>(window).platform_window(), &wmi)) { + osd_printf_error("BGFX: Error getting SDL window info: %s\n", SDL_GetError()); return false; } -# if BX_PLATFORM_LINUX || BX_PLATFORM_BSD - platform_data.ndt = wmi.info.x11.display; - platform_data.nwh = (void*)(uintptr_t)wmi.info.x11.window; -# elif BX_PLATFORM_OSX - platform_data.ndt = NULL; - platform_data.nwh = wmi.info.cocoa.window; -# elif BX_PLATFORM_WINDOWS - platform_data.ndt = NULL; - platform_data.nwh = wmi.info.win.window; -# endif // BX_PLATFORM_ - platform_data.context = NULL; - platform_data.backBuffer = NULL; - platform_data.backBufferDS = NULL; + switch (wmi.subsystem) + { +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + case SDL_SYSWM_WINDOWS: + platform_data.ndt = nullptr; + platform_data.nwh = wmi.info.win.window; + break; +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + case SDL_SYSWM_X11: + platform_data.ndt = wmi.info.x11.display; + platform_data.nwh = (void *)uintptr_t(wmi.info.x11.window); + break; +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + case SDL_SYSWM_COCOA: + platform_data.ndt = nullptr; + 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) + 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); + if (!platform_data.nwh) + { + osd_printf_error("BGFX: Error creating a Wayland window\n"); + return false; + } + platform_data.type = bgfx::NativeWindowHandleType::Wayland; + break; +#endif +#if defined(SDL_VIDEO_DRIVER_ANDROID) + case SDL_SYSWM_ANDROID: + platform_data.ndt = nullptr; + platform_data.nwh = wmi.info.android.window; + break; +#endif + default: + osd_printf_error("BGFX: Unsupported SDL window manager type %u\n", wmi.subsystem); + return false; + } +#endif // defined(OSD_*) + + platform_data.context = nullptr; + platform_data.backBuffer = nullptr; + platform_data.backBufferDS = nullptr; bgfx::setPlatformData(platform_data); return true; } -#endif + +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(RENDERER_BGFX, osd::video_bgfx) //============================================================ -// renderer_bgfx::create +// CONSTANTS //============================================================ -int renderer_bgfx::create() +uint16_t const renderer_bgfx::CACHE_SIZE = 1024; +uint32_t const renderer_bgfx::PACKABLE_SIZE = 128; +uint32_t const renderer_bgfx::WHITE_HASH = 0x87654321; +char const *const renderer_bgfx::WINDOW_PREFIX = "Window 0, "; + + + +//============================================================ +// MACROS +//============================================================ + +#define GIBBERISH (0) +#define SCENE_VIEW (0) + + + +//============================================================ +// STATICS +//============================================================ + +uint32_t renderer_bgfx::s_current_view = 0; +uint32_t renderer_bgfx::s_width[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +uint32_t renderer_bgfx::s_height[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + + +//============================================================ +// helper for getting native platform window +//============================================================ + +#ifdef OSD_SDL +static std::pair<void *, bool> sdlNativeWindowHandle(SDL_Window *window) { - std::shared_ptr<osd_window> win = assert_window(); - osd_dim wdim = win->get_size(); - s_width[win->index()] = wdim.width(); - s_height[win->index()] = wdim.height(); - m_dimensions = wdim; + SDL_SysWMinfo wmi; + SDL_VERSION(&wmi.version); + if (!SDL_GetWindowWMInfo(window, &wmi)) + return std::make_pair(nullptr, false); - if (s_bgfx_library_initialized) + switch (wmi.subsystem) { - exit(); +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + case SDL_SYSWM_WINDOWS: + return std::make_pair(wmi.info.win.window, true); +#endif +#if defined(SDL_VIDEO_DRIVER_X11) + case SDL_SYSWM_X11: + return std::make_pair((void *)uintptr_t(wmi.info.x11.window), true); +#endif +#if defined(SDL_VIDEO_DRIVER_COCOA) + 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) + 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); + } +#endif +#if defined(SDL_VIDEO_DRIVER_ANDROID) + case SDL_SYSWM_ANDROID: + return std::make_pair(wmi.info.android.window, true); +#endif + default: + return std::make_pair(nullptr, false); } +} +#endif // OSD_SDL + + + +//============================================================ +// renderer_bgfx - constructor +//============================================================ - if (win->index() == 0) +renderer_bgfx::renderer_bgfx(osd_window &window, parent_module &parent) + : osd_renderer(window) + , m_module(parent) + , m_framebuffer(nullptr) + , m_texture_cache(nullptr) + , m_dimensions(0, 0) + , m_max_view(0) + , m_avi_view(nullptr) + , m_avi_writer(nullptr) + , m_avi_target(nullptr) + , m_load_sub(parent.subscribe_load(&renderer_bgfx::load_config, this)) + , m_save_sub(parent.subscribe_save(&renderer_bgfx::save_config, this)) +{ + // load settings if recreated after fullscreen toggle + util::xml::data_node *windownode = m_module().persistent_settings().get_child("window"); + while (windownode) { -#ifdef OSD_WINDOWS - winSetHwnd(m_platform_data, std::static_pointer_cast<win_window_info>(win)->platform_window()); -#elif defined(OSD_MAC) - macSetWindow(m_platform_data, std::static_pointer_cast<mac_window_info>(win)->platform_window()); -#else - sdlSetWindow(m_platform_data, std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window()); -#endif + if (windownode->get_attribute_int("index", -1) != window.index()) + { + windownode = windownode->get_next_sibling("window"); + } + else + { + if (!m_config) + { + m_config = util::xml::file::create(); + windownode->copy_into(*m_config); + } + std::exchange(windownode, windownode->get_next_sibling("window"))->delete_node(); + } + } +} + - init_bgfx_library(); + +//============================================================ +// renderer_bgfx - destructor +//============================================================ + +renderer_bgfx::~renderer_bgfx() +{ + // persist settings across fullscreen toggle + if (m_config) + m_config->get_first_child()->copy_into(m_module().persistent_settings()); + else if (m_chains) + m_chains->save_config(m_module().persistent_settings()); + + bgfx::reset(0, 0, BGFX_RESET_NONE); + + if (m_avi_writer != nullptr && m_avi_writer->recording()) + { + m_avi_writer->stop(); + + m_targets->destroy_target("avibuffer0"); + m_avi_target = nullptr; + + bgfx::destroy(m_avi_texture); + + delete m_avi_writer; + delete [] m_avi_data; + delete m_avi_view; } +} - // finish creating the renderer - m_textures = new texture_manager(); - m_targets = new target_manager(*m_textures); - m_shaders = new shader_manager(); - m_effects = new effect_manager(*m_shaders); - // Create program from shaders. - m_gui_effect[0] = m_effects->get_or_load_effect(m_options, "gui_opaque"); - m_gui_effect[1] = m_effects->get_or_load_effect(m_options, "gui_blend"); - m_gui_effect[2] = m_effects->get_or_load_effect(m_options, "gui_multiply"); - m_gui_effect[3] = m_effects->get_or_load_effect(m_options, "gui_add"); +//============================================================ +// renderer_bgfx::create +//============================================================ + +int renderer_bgfx::create() +{ + const osd_dim wdim = window().get_size(); + s_width[window().index()] = wdim.width(); + s_height[window().index()] = wdim.height(); + m_dimensions = wdim; - m_screen_effect[0] = m_effects->get_or_load_effect(m_options, "screen_opaque"); - m_screen_effect[1] = m_effects->get_or_load_effect(m_options, "screen_blend"); - m_screen_effect[2] = m_effects->get_or_load_effect(m_options, "screen_multiply"); - m_screen_effect[3] = m_effects->get_or_load_effect(m_options, "screen_add"); + // finish creating the renderer + m_textures = std::make_unique<texture_manager>(); + m_targets = std::make_unique<target_manager>(*m_textures); - if (win->index() != 0) + if (window().index() != 0) { #ifdef OSD_WINDOWS - m_framebuffer = m_targets->create_backbuffer(std::static_pointer_cast<win_window_info>(win)->platform_window(), s_width[win->index()], s_height[win->index()]); + m_framebuffer = m_targets->create_backbuffer(dynamic_cast<win_window_info &>(window()).platform_window(), s_width[window().index()], s_height[window().index()]); #elif defined(OSD_MAC) - m_framebuffer = m_targets->create_backbuffer(GetOSWindow(std::static_pointer_cast<mac_window_info>(win)->platform_window()), s_width[win->index()], s_height[win->index()]); + m_framebuffer = m_targets->create_backbuffer(GetOSWindow(dynamic_cast<mac_window_info &>(window()).platform_window()), s_width[window().index()], s_height[window().index()]); #else - m_framebuffer = m_targets->create_backbuffer(sdlNativeWindowHandle(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window()), s_width[win->index()], s_height[win->index()]); + auto const [winhdl, success] = sdlNativeWindowHandle(dynamic_cast<sdl_window_info &>(window()).platform_window()); + if (!success) + { + m_targets.reset(); + m_textures.reset(); + return -1; + } + m_framebuffer = m_targets->create_backbuffer(winhdl, s_width[window().index()], s_height[window().index()]); #endif - bgfx::touch(win->index()); + bgfx::touch(window().index()); - if (m_ortho_view) { + if (m_ortho_view) m_ortho_view->set_backbuffer(m_framebuffer); - } } - m_chains = new chain_manager(win->machine(), m_options, *m_textures, *m_targets, *m_effects, win->index(), *this); + m_shaders = std::make_unique<shader_manager>(); + m_effects = std::make_unique<effect_manager>(*m_shaders); + + // Create program from shaders. + m_gui_effect[0] = m_effects->get_or_load_effect(m_module().options(), "gui_opaque"); + m_gui_effect[1] = m_effects->get_or_load_effect(m_module().options(), "gui_blend"); + m_gui_effect[2] = m_effects->get_or_load_effect(m_module().options(), "gui_multiply"); + m_gui_effect[3] = m_effects->get_or_load_effect(m_module().options(), "gui_add"); + + m_screen_effect[0] = m_effects->get_or_load_effect(m_module().options(), "screen_opaque"); + m_screen_effect[1] = m_effects->get_or_load_effect(m_module().options(), "screen_blend"); + m_screen_effect[2] = m_effects->get_or_load_effect(m_module().options(), "screen_multiply"); + m_screen_effect[3] = m_effects->get_or_load_effect(m_module().options(), "screen_add"); + + const uint32_t max_prescale_size = std::min(2u * std::max(wdim.width(), wdim.height()), m_module().max_texture_size()); + m_chains = std::make_unique<chain_manager>( + window().machine(), + m_module().options(), + *m_textures, + *m_targets, + *m_effects, + window().index(), + *this, + window().prescale(), + max_prescale_size); m_sliders_dirty = true; uint32_t flags = BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT; - m_texture_cache = m_textures->create_texture("#cache", bgfx::TextureFormat::BGRA8, CACHE_SIZE, CACHE_SIZE, nullptr, flags); + m_texture_cache = m_textures->create_texture("#cache", bgfx::TextureFormat::BGRA8, CACHE_SIZE, 0, CACHE_SIZE, nullptr, flags); memset(m_white, 0xff, sizeof(uint32_t) * 16 * 16); m_texinfo.push_back(rectangle_packer::packable_rectangle(WHITE_HASH, PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32), 16, 16, 16, nullptr, m_white)); @@ -365,16 +723,12 @@ int renderer_bgfx::create() void renderer_bgfx::record() { - std::shared_ptr<osd_window> win = assert_window(); - - if (win->index() > 0) - { + if (window().index() > 0) return; - } if (m_avi_writer == nullptr) { - m_avi_writer = new avi_write(win->machine(), s_width[0], s_height[0]); + m_avi_writer = new avi_write(window().machine(), s_width[0], s_height[0]); m_avi_data = new uint8_t[s_width[0] * s_height[0] * 4]; m_avi_bitmap.allocate(s_width[0], s_height[0]); } @@ -390,8 +744,8 @@ void renderer_bgfx::record() } else { - m_avi_writer->record(m_options.bgfx_avi_name()); - m_avi_target = m_targets->create_target("avibuffer", bgfx::TextureFormat::BGRA8, s_width[0], s_height[0], TARGET_STYLE_CUSTOM, false, true, 1, 0); + m_avi_writer->record(m_module().options().bgfx_avi_name()); + m_avi_target = m_targets->create_target("avibuffer", bgfx::TextureFormat::BGRA8, s_width[0], s_height[0], 1, 1, TARGET_STYLE_CUSTOM, false, true, 1, 0); m_avi_texture = bgfx::createTexture2D(s_width[0], s_height[0], false, 1, bgfx::TextureFormat::BGRA8, BGFX_TEXTURE_BLIT_DST | BGFX_TEXTURE_READ_BACK); if (m_avi_view == nullptr) @@ -401,47 +755,6 @@ void renderer_bgfx::record() } } -bool renderer_bgfx::init(running_machine &machine) -{ - osd_options &options = downcast<osd_options &>(machine.options()); - const char *bgfx_path = options.bgfx_path(); - - osd::directory::ptr directory = osd::directory::open(bgfx_path); - if (directory == nullptr) - { - osd_printf_error("Unable to find the BGFX path %s, please install it or fix the bgfx_path setting to use the BGFX renderer.\n", bgfx_path); - return true; - } - - // Verify baseline shaders. - const bool gui_opaque_valid = effect_manager::validate_effect(options, "gui_opaque"); - const bool gui_blend_valid = effect_manager::validate_effect(options, "gui_blend"); - const bool gui_multiply_valid = effect_manager::validate_effect(options, "gui_multiply"); - const bool gui_add_valid = effect_manager::validate_effect(options, "gui_add"); - const bool all_gui_valid = gui_opaque_valid && gui_blend_valid && gui_multiply_valid && gui_add_valid; - - const bool screen_opaque_valid = effect_manager::validate_effect(options, "screen_opaque"); - const bool screen_blend_valid = effect_manager::validate_effect(options, "screen_blend"); - const bool screen_multiply_valid = effect_manager::validate_effect(options, "screen_multiply"); - const bool screen_add_valid = effect_manager::validate_effect(options, "screen_add"); - const bool all_screen_valid = screen_opaque_valid && screen_blend_valid && screen_multiply_valid && screen_add_valid; - - if (!all_gui_valid || !all_screen_valid) - { - osd_printf_error("BGFX: Unable to load required shaders. Please update the %s folder or adjust your bgfx_path setting.\n", options.bgfx_path()); - return true; - } - - return false; -} - -void renderer_bgfx::exit() -{ - imguiDestroy(); - bgfx::shutdown(); - s_bgfx_library_initialized = false; -} - //============================================================ // drawsdl_xy_to_render_target //============================================================ @@ -480,15 +793,6 @@ void renderer_bgfx::put_packed_quad(render_primitive *prim, uint32_t hash, Scree float u[4] = { u0, u1, u0, u1 }; float v[4] = { v0, v0, v1, v1 }; - if (bgfx::getRendererType() == bgfx::RendererType::Direct3D9) - { - for (int i = 0; i < 4; i++) - { - u[i] += 0.5f / size; - v[i] += 0.5f / size; - } - } - if (PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_SWAP_XY) { std::swap(u[1], u[2]); @@ -529,7 +833,7 @@ void renderer_bgfx::vertex(ScreenVertex* vertex, float x, float y, float z, uint vertex->m_v = v; } -void renderer_bgfx::render_post_screen_quad(int view, render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int32_t screen) +void renderer_bgfx::render_post_screen_quad(int view, render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int32_t screen, int window_index) { auto* vertices = reinterpret_cast<ScreenVertex*>(buffer->data); @@ -554,6 +858,15 @@ void renderer_bgfx::render_post_screen_quad(int view, render_primitive* prim, bg uint32_t blend = PRIMFLAG_GET_BLENDMODE(prim->flags); bgfx::setVertexBuffer(0,buffer); bgfx::setTexture(0, m_screen_effect[blend]->uniform("s_tex")->handle(), m_targets->target(screen, "output")->texture(), texture_flags); + + bgfx_uniform* inv_view_dims = m_screen_effect[blend]->uniform("u_inv_view_dims"); + if (inv_view_dims) + { + float values[2] = { -1.0f / s_width[window_index], 1.0f / s_height[window_index] }; + inv_view_dims->set(values, sizeof(float) * 2); + inv_view_dims->upload(); + } + m_screen_effect[blend]->submit(m_ortho_view->get_index()); } @@ -563,7 +876,7 @@ void renderer_bgfx::render_avi_quad() m_avi_view->setup(); bgfx::setViewRect(s_current_view, 0, 0, s_width[0], s_height[0]); - bgfx::setViewClear(s_current_view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x00000000, 1.0f, 0); + bgfx::setViewClear(s_current_view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000ff, 1.0f, 0); bgfx::TransientVertexBuffer buffer; bgfx::allocTransientVertexBuffer(&buffer, 6, ScreenVertex::ms_decl); @@ -584,11 +897,21 @@ void renderer_bgfx::render_avi_quad() bgfx::setVertexBuffer(0,&buffer); bgfx::setTexture(0, m_gui_effect[PRIMFLAG_GET_BLENDMODE(BLENDMODE_NONE)]->uniform("s_tex")->handle(), m_avi_target->texture()); - m_gui_effect[PRIMFLAG_GET_BLENDMODE(BLENDMODE_NONE)]->submit(s_current_view); + + bgfx_effect* effect = m_gui_effect[PRIMFLAG_GET_BLENDMODE(BLENDMODE_NONE)]; + bgfx_uniform* inv_view_dims = effect->uniform("u_inv_view_dims"); + if (inv_view_dims) + { + float values[2] = { -1.0f / s_width[0], 1.0f / s_height[0] }; + inv_view_dims->set(values, sizeof(float) * 2); + inv_view_dims->upload(); + } + + effect->submit(s_current_view); s_current_view++; } -void renderer_bgfx::render_textured_quad(render_primitive* prim, bgfx::TransientVertexBuffer* buffer) +void renderer_bgfx::render_textured_quad(render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int window_index) { auto* vertices = reinterpret_cast<ScreenVertex*>(buffer->data); uint32_t rgba = u32Color(prim->color.r * 255, prim->color.g * 255, prim->color.b * 255, prim->color.a * 255); @@ -625,7 +948,7 @@ void renderer_bgfx::render_textured_quad(render_primitive* prim, bgfx::Transient else { texture = m_textures->create_or_update_mame_texture(prim->flags & PRIMFLAG_TEXFORMAT_MASK - , tex_width, tex_height, prim->texture.rowpixels, prim->texture.palette, prim->texture.base, prim->texture.seqid + , tex_width, prim->texture.width_margin, tex_height, prim->texture.rowpixels, prim->texture.palette, prim->texture.base, prim->texture.seqid , texture_flags, prim->texture.unique_id, prim->texture.old_id); } @@ -634,6 +957,15 @@ void renderer_bgfx::render_textured_quad(render_primitive* prim, bgfx::Transient uint32_t blend = PRIMFLAG_GET_BLENDMODE(prim->flags); bgfx::setVertexBuffer(0,buffer); bgfx::setTexture(0, effects[blend]->uniform("s_tex")->handle(), texture); + + bgfx_uniform* inv_view_dims = effects[blend]->uniform("u_inv_view_dims"); + if (inv_view_dims) + { + float values[2] = { -1.0f / s_width[window_index], 1.0f / s_height[window_index] }; + inv_view_dims->set(values, sizeof(float) * 2); + inv_view_dims->upload(); + } + effects[blend]->submit(m_ortho_view->get_index()); if (is_screen) @@ -852,16 +1184,13 @@ uint32_t renderer_bgfx::u32Color(uint32_t r, uint32_t g, uint32_t b, uint32_t a int renderer_bgfx::draw(int update) { - std::shared_ptr<osd_window> win = assert_window(); - - int window_index = win->index(); + int window_index = window().index(); m_seen_views.clear(); - if (m_ortho_view) { + if (m_ortho_view) m_ortho_view->set_index(UINT_MAX); - } - osd_dim wdim = win->get_size(); + osd_dim wdim = window().get_size(); s_width[window_index] = wdim.width(); s_height[window_index] = wdim.height(); @@ -871,9 +1200,9 @@ int renderer_bgfx::draw(int update) s_current_view = 0; } - win->m_primlist->acquire_lock(); - uint32_t num_screens = m_chains->update_screen_textures(s_current_view, win->m_primlist->first(), *win.get()); - win->m_primlist->release_lock(); + window().m_primlist->acquire_lock(); + uint32_t num_screens = m_chains->update_screen_textures(s_current_view, window().m_primlist->first(), window()); + window().m_primlist->release_lock(); bool skip_frame = update_dimensions(); if (skip_frame) @@ -883,7 +1212,21 @@ int renderer_bgfx::draw(int update) if (num_screens) { - uint32_t chain_view_count = m_chains->process_screen_chains(s_current_view, *win.get()); + // Restore config after counting screens the first time + // Doing this here is hacky - it means config is restored at the wrong + // time if the initial view has no screens and the user switches to a + // view with screens. The trouble is there's no real interface between + // the render targets and the renderer so we don't actually know when + // we're first called on to render a live view (as opposed to an info + // screen). + if (m_config) + { + osd_printf_verbose("BGFX: Applying configuration for window %d\n", window().index()); + m_chains->load_config(*m_config->get_first_child()); + m_config.reset(); + } + + uint32_t chain_view_count = m_chains->process_screen_chains(s_current_view, window()); s_current_view += chain_view_count; } @@ -896,12 +1239,12 @@ int renderer_bgfx::draw(int update) s_current_view = m_max_view; } - win->m_primlist->acquire_lock(); + window().m_primlist->acquire_lock(); // Mark our texture atlas as dirty if we need to do so bool atlas_valid = update_atlas(); - render_primitive *prim = win->m_primlist->first(); + render_primitive *prim = window().m_primlist->first(); std::vector<void*> sources; while (prim != nullptr) { @@ -926,12 +1269,21 @@ int renderer_bgfx::draw(int update) } } - buffer_status status = buffer_primitives(atlas_valid, &prim, &buffer, screen); + buffer_status status = buffer_primitives(atlas_valid, &prim, &buffer, screen, window_index); if (status != BUFFER_EMPTY && status != BUFFER_SCREEN) { - bgfx::setVertexBuffer(0,&buffer); + bgfx::setVertexBuffer(0, &buffer); bgfx::setTexture(0, m_gui_effect[blend]->uniform("s_tex")->handle(), m_texture_cache->texture()); + + bgfx_uniform* inv_view_dims = m_gui_effect[blend]->uniform("u_inv_view_dims"); + if (inv_view_dims) + { + float values[2] = { -1.0f / s_width[window_index], 1.0f / s_height[window_index] }; + inv_view_dims->set(values, sizeof(float) * 2); + inv_view_dims->upload(); + } + m_gui_effect[blend]->submit(m_ortho_view->get_index()); } @@ -941,7 +1293,7 @@ int renderer_bgfx::draw(int update) } } - win->m_primlist->release_lock(); + window().m_primlist->release_lock(); // This dummy draw call is here to make sure that view 0 is cleared // if no other draw calls are submitted to view 0. @@ -959,7 +1311,7 @@ int renderer_bgfx::draw(int update) } } - if (win->index() == osd_common_t::s_window_list.size() - 1) + if (window().index() == osd_common_t::window_list().size() - 1) { bgfx::frame(); } @@ -989,8 +1341,7 @@ void renderer_bgfx::update_recording() void renderer_bgfx::add_audio_to_recording(const int16_t *buffer, int samples_this_frame) { - std::shared_ptr<osd_window> win = assert_window(); - if (m_avi_writer != nullptr && m_avi_writer->recording() && win->index() == 0) + if (m_avi_writer != nullptr && m_avi_writer->recording() && window().index() == 0) { m_avi_writer->audio_frame(buffer, samples_this_frame); } @@ -998,9 +1349,7 @@ void renderer_bgfx::add_audio_to_recording(const int16_t *buffer, int samples_th bool renderer_bgfx::update_dimensions() { - std::shared_ptr<osd_window> win = assert_window(); - - const uint32_t window_index = win->index(); + const uint32_t window_index = window().index(); const uint32_t width = s_width[window_index]; const uint32_t height = s_height[window_index]; @@ -1009,15 +1358,14 @@ bool renderer_bgfx::update_dimensions() bgfx::reset(width, height, video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE); m_dimensions = osd_dim(width, height); - if (win->index() != 0) + if (window().index() != 0) { - delete m_framebuffer; #ifdef OSD_WINDOWS - m_framebuffer = m_targets->create_backbuffer(std::static_pointer_cast<win_window_info>(win)->platform_window(), width, height); + m_framebuffer = m_targets->create_backbuffer(dynamic_cast<win_window_info &>(window()).platform_window(), width, height); #elif defined(OSD_MAC) - m_framebuffer = m_targets->create_backbuffer(GetOSWindow(std::static_pointer_cast<mac_window_info>(win)->platform_window()), width, height); + m_framebuffer = m_targets->create_backbuffer(GetOSWindow(dynamic_cast<mac_window_info &>(window()).platform_window()), width, height); #else - m_framebuffer = m_targets->create_backbuffer(sdlNativeWindowHandle(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window()), width, height); + m_framebuffer = m_targets->create_backbuffer(sdlNativeWindowHandle(dynamic_cast<sdl_window_info &>(window()).platform_window()).first, width, height); #endif if (m_ortho_view) { @@ -1026,7 +1374,7 @@ bool renderer_bgfx::update_dimensions() bgfx::setViewFrameBuffer(s_current_view, m_framebuffer->target()); } - bgfx::setViewClear(s_current_view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x00000000, 1.0f, 0); + bgfx::setViewClear(s_current_view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x000000ff, 1.0f, 0); bgfx::setViewMode(s_current_view, bgfx::ViewMode::Sequential); bgfx::touch(s_current_view); bgfx::frame(); @@ -1039,7 +1387,7 @@ void renderer_bgfx::setup_ortho_view() { if (!m_ortho_view) { - m_ortho_view = new bgfx_ortho_view(this, 0, m_framebuffer, m_seen_views); + m_ortho_view = std::make_unique<bgfx_ortho_view>(this, 0, m_framebuffer, m_seen_views); } if (m_ortho_view->get_index() == UINT_MAX) { @@ -1052,10 +1400,6 @@ void renderer_bgfx::setup_ortho_view() render_primitive_list *renderer_bgfx::get_primitives() { - auto win = try_getwindow(); - if (win == nullptr) - return nullptr; - // determines whether the screen container is transformed by the chain's shaders bool chain_transform = false; @@ -1066,15 +1410,15 @@ render_primitive_list *renderer_bgfx::get_primitives() chain_transform = chain->transform(); } - osd_dim wdim = win->get_size(); + osd_dim wdim = window().get_size(); if (wdim.width() > 0 && wdim.height() > 0) - win->target()->set_bounds(wdim.width(), wdim.height(), win->pixel_aspect()); + window().target()->set_bounds(wdim.width(), wdim.height(), window().pixel_aspect()); - win->target()->set_transform_container(!chain_transform); - return &win->target()->get_primitives(); + window().target()->set_transform_container(!chain_transform); + return &window().target()->get_primitives(); } -renderer_bgfx::buffer_status renderer_bgfx::buffer_primitives(bool atlas_valid, render_primitive** prim, bgfx::TransientVertexBuffer* buffer, int32_t screen) +renderer_bgfx::buffer_status renderer_bgfx::buffer_primitives(bool atlas_valid, render_primitive** prim, bgfx::TransientVertexBuffer* buffer, int32_t screen, int window_index) { int vertices = 0; @@ -1116,18 +1460,18 @@ renderer_bgfx::buffer_status renderer_bgfx::buffer_primitives(bool atlas_valid, { #if SCENE_VIEW setup_view(s_current_view, true); - render_post_screen_quad(s_current_view, *prim, buffer, screen); + render_post_screen_quad(s_current_view, *prim, buffer, screen, window_index); s_current_view++; #else setup_ortho_view(); - render_post_screen_quad(m_ortho_view->get_index(), *prim, buffer, screen); + render_post_screen_quad(m_ortho_view->get_index(), *prim, buffer, screen, window_index); #endif return BUFFER_SCREEN; } else { setup_ortho_view(); - render_textured_quad(*prim, buffer); + render_textured_quad(*prim, buffer, window_index); return BUFFER_EMPTY; } } @@ -1208,7 +1552,7 @@ void renderer_bgfx::process_atlas_packs(std::vector<std::vector<rectangle_packer uint16_t pitch = rect.width(); int width_div_factor = 1; int width_mul_factor = 1; - const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, rect.format(), rect.rowpixels(), rect.height(), rect.palette(), rect.base(), pitch, width_div_factor, width_mul_factor); + const bgfx::Memory* mem = bgfx_util::mame_texture_data_to_bgfx_texture_data(dst_format, rect.format(), rect.rowpixels(), 0, rect.height(), rect.palette(), rect.base(), pitch, width_div_factor, width_mul_factor); bgfx::updateTexture2D(m_texture_cache->texture(), 0, 0, rect.x(), rect.y(), (rect.width() * width_mul_factor) / width_div_factor, rect.height(), mem, pitch); } } @@ -1246,9 +1590,8 @@ bool renderer_bgfx::check_for_dirty_atlas() { bool atlas_dirty = false; - std::shared_ptr<osd_window> win = assert_window(); std::map<uint32_t, rectangle_packer::packable_rectangle> acquired_infos; - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { bool pack = prim.packable(PACKABLE_SIZE); if (prim.type == render_primitive::QUAD && prim.texture.base != nullptr && pack) @@ -1261,8 +1604,7 @@ bool renderer_bgfx::check_for_dirty_atlas() atlas_dirty = true; m_texinfo.push_back(rectangle_packer::packable_rectangle(hash, prim.flags & PRIMFLAG_TEXFORMAT_MASK, - prim.texture.width, prim.texture.height, - prim.texture.rowpixels, prim.texture.palette, prim.texture.base)); + prim.texture.width, prim.texture.height, prim.texture.rowpixels, prim.texture.palette, prim.texture.base)); acquired_infos[hash] = m_texinfo[m_texinfo.size() - 1]; } } @@ -1339,11 +1681,44 @@ void renderer_bgfx::set_sliders_dirty() m_sliders_dirty = true; } -uint32_t renderer_bgfx::get_window_width(uint32_t index) const { +uint32_t renderer_bgfx::get_window_width(uint32_t index) const +{ return s_width[index]; } -uint32_t renderer_bgfx::get_window_height(uint32_t index) const { +uint32_t renderer_bgfx::get_window_height(uint32_t index) const +{ return s_height[index]; } + +void renderer_bgfx::load_config(util::xml::data_node const &parentnode) +{ + util::xml::data_node const *windownode = parentnode.get_child("window"); + while (windownode) + { + if (windownode->get_attribute_int("index", -1) != window().index()) + { + windownode = windownode->get_next_sibling("window"); + continue; + } + + if (!m_config) + m_config = util::xml::file::create(); + else + m_config->get_first_child()->delete_node(); + windownode->copy_into(*m_config); + m_config->get_first_child()->set_attribute("persist", "0"); + osd_printf_verbose("BGFX: Found configuration for window %d\n", window().index()); + break; + } +} + + +void renderer_bgfx::save_config(util::xml::data_node &parentnode) +{ + if (m_config) + m_config->get_first_child()->copy_into(parentnode); + else + m_chains->save_config(parentnode); +} diff --git a/src/osd/modules/render/drawbgfx.h b/src/osd/modules/render/drawbgfx.h index fc728e41d67..bb9f538aafa 100644 --- a/src/osd/modules/render/drawbgfx.h +++ b/src/osd/modules/render/drawbgfx.h @@ -1,30 +1,34 @@ // license:BSD-3-Clause // copyright-holders:Ryan Holtz +#ifndef MAME_RENDER_DRAWBGFX_H +#define MAME_RENDER_DRAWBGFX_H + #pragma once -#ifndef RENDER_BGFX -#define RENDER_BGFX +#include "binpacker.h" +#include "bgfx/chain.h" +#include "bgfx/chainmanager.h" +#include "bgfx/vertex.h" +#include "sliderdirtynotifier.h" + +#include "modules/osdwindow.h" + +#include "notifier.h" #include <bgfx/bgfx.h> #include <map> +#include <memory> #include <vector> -#include "binpacker.h" -#include "bgfx/vertex.h" -#include "bgfx/chain.h" -#include "bgfx/chainmanager.h" -#include "sliderdirtynotifier.h" class texture_manager; class target_manager; class shader_manager; class effect_manager; -class chain_manager; class bgfx_texture; class bgfx_effect; class bgfx_target; -class bgfx_chain; class bgfx_view; class osd_options; class avi_write; @@ -33,11 +37,10 @@ class avi_write; class renderer_bgfx : public osd_renderer, public slider_dirty_notifier { public: - renderer_bgfx(std::shared_ptr<osd_window> w); - virtual ~renderer_bgfx(); + class parent_module; - static bool init(running_machine &machine); - static void exit(); + renderer_bgfx(osd_window &window, parent_module &parent_module); + virtual ~renderer_bgfx(); virtual int create() override; virtual int draw(const int update) override; @@ -62,7 +65,24 @@ public: static char const *const WINDOW_PREFIX; private: - void init_bgfx_library(); + enum buffer_status + { + BUFFER_PRE_FLUSH, + BUFFER_FLUSH, + BUFFER_SCREEN, + BUFFER_EMPTY, + BUFFER_DONE + }; + + class parent_module_holder + { + public: + parent_module_holder(parent_module &parent); + ~parent_module_holder(); + parent_module &operator()() const { return m_parent; } + private: + parent_module &m_parent; + }; void vertex(ScreenVertex* vertex, float x, float y, float z, uint32_t rgba, float u, float v); void render_avi_quad(); @@ -73,18 +93,10 @@ private: void setup_ortho_view(); void allocate_buffer(render_primitive *prim, uint32_t blend, bgfx::TransientVertexBuffer *buffer); - enum buffer_status - { - BUFFER_PRE_FLUSH, - BUFFER_FLUSH, - BUFFER_SCREEN, - BUFFER_EMPTY, - BUFFER_DONE - }; - buffer_status buffer_primitives(bool atlas_valid, render_primitive** prim, bgfx::TransientVertexBuffer* buffer, int32_t screen); + buffer_status buffer_primitives(bool atlas_valid, render_primitive** prim, bgfx::TransientVertexBuffer* buffer, int32_t screen, int window_index); - void render_textured_quad(render_primitive* prim, bgfx::TransientVertexBuffer* buffer); - void render_post_screen_quad(int view, render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int32_t screen); + void render_textured_quad(render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int window_index); + void render_post_screen_quad(int view, render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int32_t screen, int window_index); void put_packed_quad(render_primitive *prim, uint32_t hash, ScreenVertex* vertex); void put_packed_line(render_primitive *prim, ScreenVertex* vertex); @@ -100,8 +112,10 @@ private: void process_atlas_packs(std::vector<std::vector<rectangle_packer::packed_rectangle>>& packed); uint32_t get_texture_hash(render_primitive *prim); - osd_options& m_options; - bgfx::PlatformData m_platform_data; + void load_config(util::xml::data_node const &parentnode); + void save_config(util::xml::data_node &parentnode); + + parent_module_holder m_module; // keep this where it will be destructed last bgfx_target *m_framebuffer; bgfx_texture *m_texture_cache; @@ -109,11 +123,11 @@ private: // Original display_mode osd_dim m_dimensions; - texture_manager *m_textures; - target_manager *m_targets; - shader_manager *m_shaders; - effect_manager *m_effects; - chain_manager *m_chains; + std::unique_ptr<texture_manager> m_textures; + std::unique_ptr<target_manager> m_targets; + std::unique_ptr<shader_manager> m_shaders; + std::unique_ptr<effect_manager> m_effects; + std::unique_ptr<chain_manager> m_chains; bgfx_effect *m_gui_effect[4]; bgfx_effect *m_screen_effect[4]; @@ -124,7 +138,7 @@ private: rectangle_packer m_packer; uint32_t m_white[16*16]; - bgfx_view *m_ortho_view; + std::unique_ptr<bgfx_view> m_ortho_view; uint32_t m_max_view; bgfx_view *m_avi_view; @@ -134,14 +148,17 @@ private: bitmap_rgb32 m_avi_bitmap; uint8_t *m_avi_data; + std::unique_ptr<util::xml::file> m_config; + const util::notifier_subscription m_load_sub; + const util::notifier_subscription m_save_sub; + static const uint16_t CACHE_SIZE; static const uint32_t PACKABLE_SIZE; static const uint32_t WHITE_HASH; static uint32_t s_current_view; - static bool s_bgfx_library_initialized; static uint32_t s_width[16]; static uint32_t s_height[16]; }; -#endif // RENDER_BGFX +#endif // MAME_RENDER_DRAWBGFX_H diff --git a/src/osd/modules/render/drawd3d.cpp b/src/osd/modules/render/drawd3d.cpp index cfc494a2be9..542e2f8955d 100644 --- a/src/osd/modules/render/drawd3d.cpp +++ b/src/osd/modules/render/drawd3d.cpp @@ -13,21 +13,132 @@ #include "rendutil.h" #include "screen.h" -#include "aviio.h" - // MAMEOS headers -#include "winmain.h" -#include "window.h" #include "drawd3d.h" -#include "modules/render/d3d/d3dhlsl.h" + +#include "d3d/d3dhlsl.h" + +#include "render_module.h" + +// from OSD implementation +#include "window.h" +#include "winmain.h" + +// general OSD headers #include "modules/monitor/monitor_module.h" + +// lib/util +#include "aviio.h" + #include <utility> + +//============================================================ +// OSD MODULE +//============================================================ + +namespace osd { + +namespace { + +class video_d3d : public osd_module, public render_module +{ +public: + video_d3d() + : osd_module(OSD_RENDERER_PROVIDER, "d3d") + , m_options(nullptr) + { + } + + virtual bool probe() override; + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override; + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return FLAG_INTERACTIVE; } + +private: + using d3d9_create_fn = IDirect3D9 *(WINAPI *)(UINT); + + dynamic_module::ptr m_d3d9_dll; + Microsoft::WRL::ComPtr<IDirect3D9> m_d3dobj; + osd_options const *m_options; +}; + + +//============================================================ +// video_d3d::probe +//============================================================ + +bool video_d3d::probe() +{ + // do a dry run of loading the Direct3D 9 DLL + return dynamic_module::open({ "d3d9.dll" })->bind<d3d9_create_fn>("Direct3DCreate9") != nullptr; +} + + +//============================================================ +// video_d3d::init +//============================================================ + +int video_d3d::init(osd_interface &osd, osd_options const &options) +{ + m_options = &options; + + m_d3d9_dll = dynamic_module::open({ "d3d9.dll" }); + auto const d3d9_create_ptr = m_d3d9_dll->bind<d3d9_create_fn>("Direct3DCreate9"); + if (!d3d9_create_ptr) + { + osd_printf_warning("Direct3D: Could not find Direct3DCreate9 function in d3d9.dll\n"); + m_d3d9_dll.reset(); + m_options = nullptr; + return -1; + } + + m_d3dobj.Attach((d3d9_create_ptr)(D3D_SDK_VERSION)); + if (!m_d3dobj) + { + osd_printf_warning("Direct3D: Unable to initialize Direct3D 9\n"); + m_d3d9_dll.reset(); + m_options = nullptr; + return -1; + } + + osd_printf_verbose("Direct3D: Using Direct3D 9\n"); + + return 0; +} + + +//============================================================ +// video_d3d::exit +//============================================================ + +void video_d3d::exit() +{ + m_d3dobj.Reset(); + m_d3d9_dll.reset(); + m_options = nullptr; +} + + //============================================================ -// TYPE DEFINITIONS +// video_d3d::create //============================================================ -typedef IDirect3D9* (WINAPI *d3d9_create_fn)(UINT); +std::unique_ptr<osd_renderer> video_d3d::create(osd_window &window) +{ + return std::make_unique<renderer_d3d9>(window, m_d3dobj); +} + +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(RENDERER_D3D, osd::video_d3d) + //============================================================ @@ -116,13 +227,6 @@ static inline uint32_t ycc_to_rgb(uint8_t y, uint8_t cb, uint8_t cr) //============================================================ -// drawd3d_init -//============================================================ - -static d3d_base *d3dintf = nullptr; // FIX ME - - -//============================================================ // drawd3d_window_init //============================================================ @@ -174,60 +278,22 @@ void renderer_d3d9::save() render_primitive_list *renderer_d3d9::get_primitives() { RECT client; - auto win = try_getwindow(); - if (win == nullptr) - return nullptr; - - HWND hWnd = std::static_pointer_cast<win_window_info>(win)->platform_window(); + HWND hWnd = dynamic_cast<win_window_info &>(window()).platform_window(); if (IsIconic(hWnd)) return nullptr; - GetClientRectExceptMenu(hWnd, &client, win->fullscreen()); + GetClientRectExceptMenu(hWnd, &client, window().fullscreen()); if (rect_width(&client) > 0 && rect_height(&client) > 0) { - win->target()->set_bounds(rect_width(&client), rect_height(&client), win->pixel_aspect()); - win->target()->set_max_update_rate((get_refresh() == 0) ? get_origmode().RefreshRate : get_refresh()); + window().target()->set_bounds(rect_width(&client), rect_height(&client), window().pixel_aspect()); + window().target()->set_max_update_rate((get_refresh() == 0) ? get_origmode().RefreshRate : get_refresh()); } if (m_shaders != nullptr) { // do not transform primitives (scale, offset) if shaders are enabled, the shaders will handle the transformation - win->target()->set_transform_container(!m_shaders->enabled()); + window().target()->set_transform_container(!m_shaders->enabled()); } - return &win->target()->get_primitives(); -} - - -//============================================================ -// renderer_d3d9::init -//============================================================ - -bool renderer_d3d9::init(running_machine &machine) -{ - d3dintf = new d3d_base; - - d3dintf->d3d9_dll = osd::dynamic_module::open({ "d3d9.dll" }); - - d3d9_create_fn d3d9_create_ptr = d3dintf->d3d9_dll->bind<d3d9_create_fn>("Direct3DCreate9"); - if (d3d9_create_ptr == nullptr) - { - delete d3dintf; - d3dintf = nullptr; - osd_printf_verbose("Direct3D: Unable to find Direct3D 9 runtime library\n"); - return true; - } - - d3dintf->d3dobj = (*d3d9_create_ptr)(D3D_SDK_VERSION); - if (d3dintf->d3dobj == nullptr) - { - delete d3dintf; - d3dintf = nullptr; - osd_printf_verbose("Direct3D: Unable to initialize Direct3D 9\n"); - return true; - } - - osd_printf_verbose("Direct3D: Using Direct3D 9\n"); - - return false; + return &window().target()->get_primitives(); } @@ -242,7 +308,12 @@ int renderer_d3d9::draw(const int update) return check; begin_frame(); + + // reset blend mode + set_blendmode(BLENDMODE_NONE); + process_primitives(); + end_frame(); return 0; @@ -254,8 +325,12 @@ void renderer_d3d9::set_texture(texture_info *texture) { m_last_texture = texture; m_last_texture_flags = (texture == nullptr ? 0 : texture->get_flags()); + if (m_shaders->enabled()) + { + m_shaders->set_texture(texture); + } + HRESULT result = m_device->SetTexture(0, (texture == nullptr) ? get_default_texture()->get_finaltex() : texture->get_finaltex()); - m_shaders->set_texture(texture); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device set_texture call\n", result); } @@ -353,15 +428,20 @@ void renderer_d3d9::set_blendmode(int blendmode) } // adjust the bits that changed + bool new_blend_enable = false; if (blendenable != m_last_blendenable) { m_last_blendenable = blendenable; + if (blendenable) + { + new_blend_enable = true; + } HRESULT result = m_device->SetRenderState(D3DRS_ALPHABLENDENABLE, blendenable); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderState call\n", result); } - if (blendop != m_last_blendop) + if (blendop != m_last_blendop || new_blend_enable) { m_last_blendop = blendop; HRESULT result = m_device->SetRenderState(D3DRS_BLENDOP, blendop); @@ -369,7 +449,7 @@ void renderer_d3d9::set_blendmode(int blendmode) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderState call\n", result); } - if (blendsrc != m_last_blendsrc) + if (blendsrc != m_last_blendsrc || new_blend_enable) { m_last_blendsrc = blendsrc; HRESULT result = m_device->SetRenderState(D3DRS_SRCBLEND, blendsrc); @@ -377,7 +457,7 @@ void renderer_d3d9::set_blendmode(int blendmode) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderState call\n", result); } - if (blenddst != m_last_blenddst) + if (blenddst != m_last_blenddst || new_blend_enable) { m_last_blenddst = blenddst; HRESULT result = m_device->SetRenderState(D3DRS_DESTBLEND, blenddst); @@ -398,14 +478,12 @@ void renderer_d3d9::reset_render_states() m_last_wrap = (D3DTEXTUREADDRESS)-1; } -d3d_texture_manager::d3d_texture_manager(renderer_d3d9 *d3d) +d3d_texture_manager::d3d_texture_manager(renderer_d3d9 &d3d, IDirect3D9 *d3dobj) + : m_renderer(d3d) + , m_default_texture(nullptr) { - m_renderer = d3d; - - m_default_texture = nullptr; - D3DCAPS9 caps; - HRESULT result = d3dintf->d3dobj->GetDeviceCaps(d3d->get_adapter(), D3DDEVTYPE_HAL, &caps); + HRESULT result = d3dobj->GetDeviceCaps(d3d.get_adapter(), D3DDEVTYPE_HAL, &caps); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during GetDeviceCaps call\n", result); @@ -417,35 +495,30 @@ d3d_texture_manager::d3d_texture_manager(renderer_d3d9 *d3d) // pick a YUV texture format m_yuv_format = D3DFMT_UYVY; - result = d3dintf->d3dobj->CheckDeviceFormat(d3d->get_adapter(), D3DDEVTYPE_HAL, d3d->get_pixel_format(), 0, D3DRTYPE_TEXTURE, D3DFMT_UYVY); + result = d3dobj->CheckDeviceFormat(d3d.get_adapter(), D3DDEVTYPE_HAL, d3d.get_pixel_format(), 0, D3DRTYPE_TEXTURE, D3DFMT_UYVY); if (FAILED(result)) { m_yuv_format = D3DFMT_YUY2; - result = d3dintf->d3dobj->CheckDeviceFormat(d3d->get_adapter(), D3DDEVTYPE_HAL, d3d->get_pixel_format(), 0, D3DRTYPE_TEXTURE, D3DFMT_YUY2); + result = d3dobj->CheckDeviceFormat(d3d.get_adapter(), D3DDEVTYPE_HAL, d3d.get_pixel_format(), 0, D3DRTYPE_TEXTURE, D3DFMT_YUY2); if (FAILED(result)) m_yuv_format = D3DFMT_A8R8G8B8; } osd_printf_verbose("Direct3D: YUV format = %s\n", (m_yuv_format == D3DFMT_YUY2) ? "YUY2" : (m_yuv_format == D3DFMT_UYVY) ? "UYVY" : "RGB"); - auto win = d3d->assert_window(); - // set the max texture size - win->target()->set_max_texture_size(m_texture_max_width, m_texture_max_height); - osd_printf_verbose("Direct3D: Max texture size = %dx%d\n", (int)m_texture_max_width, (int)m_texture_max_height); + d3d.window().target()->set_max_texture_size(m_texture_max_width, m_texture_max_height); + osd_printf_verbose("Direct3D: Max texture size = %dx%d\n", m_texture_max_width, m_texture_max_height); } void d3d_texture_manager::create_resources() { - auto win = m_renderer->assert_window(); - m_default_bitmap.allocate(8, 8); - m_default_bitmap.fill(rgb_t(0xff,0xff,0xff,0xff)); - if (m_default_bitmap.valid()) { - render_texinfo texture; + m_default_bitmap.fill(rgb_t(0xff,0xff,0xff,0xff)); // fake in the basic data so it looks like it came from render.c + render_texinfo texture; texture.base = m_default_bitmap.raw_pixptr(0); texture.rowpixels = m_default_bitmap.rowpixels(); texture.width = m_default_bitmap.width(); @@ -456,7 +529,7 @@ void d3d_texture_manager::create_resources() texture.old_id = ~0ULL; // now create it - auto tex = std::make_unique<texture_info>(this, &texture, win->prescale(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); + auto tex = std::make_unique<texture_info>(*this, &texture, m_renderer.window().prescale(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); m_default_texture = tex.get(); m_texture_list.push_back(std::move(tex)); } @@ -465,7 +538,6 @@ void d3d_texture_manager::create_resources() void d3d_texture_manager::delete_resources() { // is part of m_texlist and will be free'd there - //delete m_default_texture; m_default_texture = nullptr; // free all textures @@ -479,16 +551,16 @@ uint32_t d3d_texture_manager::texture_compute_hash(const render_texinfo *texture texture_info *d3d_texture_manager::find_texinfo(const render_texinfo *texinfo, uint32_t flags) { - uint32_t hash = texture_compute_hash(texinfo, flags); + const uint32_t hash = texture_compute_hash(texinfo, flags); // find a match for (auto it = m_texture_list.begin(); it != m_texture_list.end(); it++) { - auto test_screen = (uint32_t)((*it)->get_texinfo().unique_id >> 57); - uint32_t test_page = (uint32_t)((*it)->get_texinfo().unique_id >> 56) & 1; - auto prim_screen = (uint32_t)(texinfo->unique_id >> 57); - uint32_t prim_page = (uint32_t)(texinfo->unique_id >> 56) & 1; - if (test_screen != prim_screen || test_page != prim_page) + const uint32_t test_screen = uint32_t((*it)->get_texinfo().unique_id >> 57); + const uint32_t test_page = uint32_t((*it)->get_texinfo().unique_id >> 56) & 1; + const uint32_t prim_screen = uint32_t(texinfo->unique_id >> 57); + const uint32_t prim_page = uint32_t(texinfo->unique_id >> 56) & 1; + if ((test_screen != prim_screen) || (test_page != prim_page)) continue; if ((*it)->get_hash() == hash && @@ -504,11 +576,35 @@ texture_info *d3d_texture_manager::find_texinfo(const render_texinfo *texinfo, u return nullptr; } -renderer_d3d9::renderer_d3d9(std::shared_ptr<osd_window> window) - : osd_renderer(window, FLAG_NONE), m_adapter(0), m_width(0), m_height(0), m_refresh(0), m_create_error_count(0), m_device(nullptr), m_gamma_supported(0), m_pixformat(), - m_vertexbuf(nullptr), m_lockedbuf(nullptr), m_numverts(0), m_vectorbatch(nullptr), m_batchindex(0), m_numpolys(0), m_toggle(false), - m_screen_format(), m_last_texture(nullptr), m_last_texture_flags(0), m_last_blendenable(0), m_last_blendop(0), m_last_blendsrc(0), m_last_blenddst(0), m_last_filter(0), - m_last_wrap(), m_last_modmode(0), m_shaders(nullptr), m_texture_manager() +renderer_d3d9::renderer_d3d9(osd_window &window, const IDirect3D9Ptr &d3dobj) + : osd_renderer(window) + , m_d3dobj(d3dobj) + , m_adapter(0) + , m_width(0) + , m_height(0) + , m_refresh(0) + , m_create_error_count(0) + , m_post_fx_available(true) + , m_gamma_supported(0) + , m_pixformat() + , m_lockedbuf(nullptr) + , m_numverts(0) + , m_vectorbatch(nullptr) + , m_batchindex(0) + , m_numpolys(0) + , m_toggle(false) + , m_screen_format() + , m_last_texture(nullptr) + , m_last_texture_flags(0) + , m_last_blendenable(0) + , m_last_blendop(0) + , m_last_blendsrc(0) + , m_last_blenddst(0) + , m_last_filter(0) + , m_last_wrap() + , m_last_modmode(0) + , m_shaders(nullptr) + , m_texture_manager() { } @@ -523,8 +619,7 @@ int renderer_d3d9::initialize() } // create the device immediately for the full screen case (defer for window mode in update_window_size()) - auto win = assert_window(); - if (win->fullscreen() && device_create(std::static_pointer_cast<win_window_info>(win->main_window())->platform_window())) + if (window().fullscreen() && device_create(dynamic_cast<win_window_info &>(window()).main_window()->platform_window())) { return false; } @@ -534,10 +629,8 @@ int renderer_d3d9::initialize() int renderer_d3d9::pre_window_draw_check() { - auto win = assert_window(); - // if we're in the middle of resizing, leave things alone - if (win->m_resize_state == RESIZE_STATE_RESIZING) + if (dynamic_cast<win_window_info &>(window()).m_resize_state == win_window_info::RESIZE_STATE_RESIZING) return 0; // check if shaders should be toggled @@ -561,7 +654,7 @@ int renderer_d3d9::pre_window_draw_check() } // if we have a device, check the cooperative level - if (m_device != nullptr) + if (m_device) { if (device_test_cooperative()) { @@ -570,14 +663,14 @@ int renderer_d3d9::pre_window_draw_check() } // in window mode, we need to track the window size - if (!win->fullscreen() || m_device == nullptr) + if (!window().fullscreen() || !m_device) { // if the size changes, skip this update since the render target will be out of date if (update_window_size()) return 0; // if we have no device, after updating the size, return an error so GDI can try - if (m_device == nullptr) + if (!m_device) return 1; } @@ -586,18 +679,16 @@ int renderer_d3d9::pre_window_draw_check() void d3d_texture_manager::update_textures() { - auto win = m_renderer->assert_window(); - - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *m_renderer.window().m_primlist) { if (prim.texture.base != nullptr) { texture_info *texture = find_texinfo(&prim.texture, prim.flags); if (texture == nullptr) { - int prescale = m_renderer->get_shaders()->enabled() ? 1 : win->prescale(); + const int prescale = m_renderer.get_shaders()->enabled() ? 1 : m_renderer.window().prescale(); - auto tex = std::make_unique<texture_info>(this, &prim.texture, prescale, prim.flags); + auto tex = std::make_unique<texture_info>(*this, &prim.texture, prescale, prim.flags); texture = tex.get(); m_texture_list.push_back(std::move(tex)); } @@ -613,21 +704,19 @@ void d3d_texture_manager::update_textures() } } - if (!m_renderer->get_shaders()->enabled()) - { + if (!m_renderer.get_shaders()->enabled()) return; - } int screen_index = 0; - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *m_renderer.window().m_primlist) { if (PRIMFLAG_GET_SCREENTEX(prim.flags)) { - if (!m_renderer->get_shaders()->get_texture_target(&prim, prim.texture.width, prim.texture.height, screen_index)) + if (!m_renderer.get_shaders()->get_texture_target(&prim, prim.texture.width, prim.texture.height, screen_index)) { - if (!m_renderer->get_shaders()->create_texture_target(&prim, prim.texture.width, prim.texture.height, screen_index)) + if (!m_renderer.get_shaders()->create_texture_target(&prim, prim.texture.width, prim.texture.height, screen_index)) { - d3dintf->post_fx_available = false; + m_renderer.set_post_fx_unavailable(); break; } } @@ -635,11 +724,11 @@ void d3d_texture_manager::update_textures() } else if (PRIMFLAG_GET_VECTORBUF(prim.flags)) { - if (!m_renderer->get_shaders()->get_vector_target(&prim, screen_index)) + if (!m_renderer.get_shaders()->get_vector_target(&prim, screen_index)) { - if (!m_renderer->get_shaders()->create_vector_target(&prim, screen_index)) + if (!m_renderer.get_shaders()->create_vector_target(&prim, screen_index)) { - d3dintf->post_fx_available = false; + m_renderer.set_post_fx_unavailable(); break; } } @@ -650,13 +739,11 @@ void d3d_texture_manager::update_textures() void renderer_d3d9::begin_frame() { - auto win = assert_window(); - - HRESULT result = m_device->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0); + HRESULT result = m_device->Clear(0, nullptr, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0xff,0,0,0), 0, 0); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device clear call\n", result); - win->m_primlist->acquire_lock(); + window().m_primlist->acquire_lock(); // first update any textures m_texture_manager->update_textures(); @@ -667,16 +754,17 @@ void renderer_d3d9::begin_frame() osd_printf_verbose("Direct3D: Error %08lX during device BeginScene call\n", result); if (m_shaders->enabled()) - m_shaders->init_fsfx_quad(); + m_shaders->begin_frame(window().m_primlist); + + // set an initial default texture + set_texture(nullptr); } void renderer_d3d9::process_primitives() { - auto win = assert_window(); - // loop over line primitives int vector_count = 0; - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { if (prim.type == render_primitive::LINE && PRIMFLAG_GET_VECTOR(prim.flags)) { @@ -685,7 +773,7 @@ void renderer_d3d9::process_primitives() } // Rotating index for vector time offsets - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { switch (prim.type) { @@ -716,13 +804,14 @@ void renderer_d3d9::process_primitives() void renderer_d3d9::end_frame() { - auto win = assert_window(); - - win->m_primlist->release_lock(); + window().m_primlist->release_lock(); // flush any pending polygons primitive_flush_pending(); + if (m_shaders->enabled()) + m_shaders->end_frame(); + // finish the scene HRESULT result = m_device->EndScene(); if (FAILED(result)) @@ -736,8 +825,6 @@ void renderer_d3d9::end_frame() void renderer_d3d9::update_presentation_parameters() { - auto win = assert_window(); - memset(&m_presentation, 0, sizeof(m_presentation)); m_presentation.BackBufferWidth = m_width; m_presentation.BackBufferHeight = m_height; @@ -745,14 +832,14 @@ void renderer_d3d9::update_presentation_parameters() m_presentation.BackBufferCount = video_config.triplebuf ? 2 : 1; m_presentation.MultiSampleType = D3DMULTISAMPLE_NONE; m_presentation.SwapEffect = D3DSWAPEFFECT_DISCARD; - m_presentation.hDeviceWindow = std::static_pointer_cast<win_window_info>(win)->platform_window(); - m_presentation.Windowed = !win->fullscreen() || win->win_has_menu(); + m_presentation.hDeviceWindow = dynamic_cast<win_window_info &>(window()).platform_window(); + m_presentation.Windowed = !window().fullscreen(); m_presentation.EnableAutoDepthStencil = FALSE; m_presentation.AutoDepthStencilFormat = D3DFMT_D16; m_presentation.Flags = 0; m_presentation.FullScreen_RefreshRateInHz = m_refresh; m_presentation.PresentationInterval = ( - (video_config.triplebuf && win->fullscreen()) + (video_config.triplebuf && window().fullscreen()) || video_config.waitvsync || video_config.syncrefresh) ? D3DPRESENT_INTERVAL_ONE @@ -767,13 +854,11 @@ void renderer_d3d9::update_gamma_ramp() return; } - auto win = assert_window(); - // set the gamma if we need to - if (win->fullscreen()) + if (window().fullscreen()) { // only set the gamma if it's not 1.0 - auto &options = downcast<windows_options &>(win->machine().options()); + auto &options = downcast<windows_options &>(window().machine().options()); float brightness = options.full_screen_brightness(); float contrast = options.full_screen_contrast(); float gamma = options.full_screen_gamma(); @@ -803,27 +888,23 @@ int renderer_d3d9::device_create(HWND hwnd) HWND device_hwnd = GetAncestor(hwnd, GA_ROOT); // if a device exists, free it - if (m_device != nullptr) - { + if (m_device) device_delete(); - } // verify the caps if (!device_verify_caps()) - { return 1; - } - m_texture_manager = std::make_unique<d3d_texture_manager>(this); + m_texture_manager = std::make_unique<d3d_texture_manager>(*this, m_d3dobj.Get()); // try for XRGB first m_screen_format = D3DFMT_X8R8G8B8; - HRESULT result = d3dintf->d3dobj->CheckDeviceFormat(m_adapter, D3DDEVTYPE_HAL, m_pixformat, D3DUSAGE_DYNAMIC, D3DRTYPE_TEXTURE, m_screen_format); + HRESULT result = m_d3dobj->CheckDeviceFormat(m_adapter, D3DDEVTYPE_HAL, m_pixformat, D3DUSAGE_DYNAMIC, D3DRTYPE_TEXTURE, m_screen_format); if (FAILED(result)) { // if not, try for ARGB m_screen_format = D3DFMT_A8R8G8B8; - result = d3dintf->d3dobj->CheckDeviceFormat(m_adapter, D3DDEVTYPE_HAL, m_pixformat, D3DUSAGE_DYNAMIC, D3DRTYPE_TEXTURE, m_screen_format); + result = m_d3dobj->CheckDeviceFormat(m_adapter, D3DDEVTYPE_HAL, m_pixformat, D3DUSAGE_DYNAMIC, D3DRTYPE_TEXTURE, m_screen_format); if (FAILED(result)) { osd_printf_error("Error: unable to configure a screen texture format\n"); @@ -835,8 +916,13 @@ int renderer_d3d9::device_create(HWND hwnd) update_presentation_parameters(); // create the D3D device - result = d3dintf->d3dobj->CreateDevice( - m_adapter, D3DDEVTYPE_HAL, device_hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &m_presentation, &m_device); + result = m_d3dobj->CreateDevice( + m_adapter, + D3DDEVTYPE_HAL, + device_hwnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, + &m_presentation, + &m_device); if (FAILED(result)) { // if we got a "DEVICELOST" error, it may be transitory; count it and only fail if @@ -869,16 +955,13 @@ int renderer_d3d9::device_create(HWND hwnd) int renderer_d3d9::device_create_resources() { - auto win = assert_window(); - // create shaders only once - if (m_shaders == nullptr) - { - m_shaders = new shaders; - } + if (!m_shaders) + m_shaders = std::make_unique<shaders>(); - if (m_shaders->init(d3dintf, &win->machine(), this)) + if (m_shaders->init(m_d3dobj.Get(), &window().machine(), this)) { + m_post_fx_available = true; m_shaders->init_slider_list(); m_sliders_dirty = true; } @@ -892,12 +975,12 @@ int renderer_d3d9::device_create_resources() // allocate a vertex buffer to use HRESULT result = m_device->CreateVertexBuffer( - sizeof(vertex) * VERTEX_BUFFER_SIZE, - D3DUSAGE_DYNAMIC | D3DUSAGE_SOFTWAREPROCESSING | D3DUSAGE_WRITEONLY, - VERTEX_BASE_FORMAT | ((m_shaders->enabled()) - ? D3DFVF_XYZW - : D3DFVF_XYZRHW), - D3DPOOL_DEFAULT, &m_vertexbuf, nullptr); + sizeof(vertex) * VERTEX_BUFFER_SIZE, + D3DUSAGE_DYNAMIC | D3DUSAGE_SOFTWAREPROCESSING | D3DUSAGE_WRITEONLY, + VERTEX_BASE_FORMAT | (m_shaders->enabled() ? D3DFVF_XYZW : D3DFVF_XYZRHW), + D3DPOOL_DEFAULT, + &m_vertexbuf, + nullptr); if (FAILED(result)) { osd_printf_error("Error creating vertex buffer (%08X)\n", (uint32_t)result); @@ -906,9 +989,7 @@ int renderer_d3d9::device_create_resources() // set the vertex format result = m_device->SetFVF( - (D3DFORMAT)(VERTEX_BASE_FORMAT | ((m_shaders->enabled()) - ? D3DFVF_XYZW - : D3DFVF_XYZRHW))); + D3DFORMAT(VERTEX_BASE_FORMAT | (m_shaders->enabled() ? D3DFVF_XYZW : D3DFVF_XYZRHW))); if (FAILED(result)) { osd_printf_error("Error setting vertex format (%08X)\n", (uint32_t)result); @@ -960,24 +1041,6 @@ int renderer_d3d9::device_create_resources() renderer_d3d9::~renderer_d3d9() { device_delete(); - - // todo: throws error when switching from full screen to window mode - //if (m_shaders != nullptr) - //{ - // // delete the HLSL interface - // delete m_shaders; - // m_shaders = nullptr; - //} -} - -void renderer_d3d9::exit() -{ - if (d3dintf != nullptr) - { - d3dintf->d3dobj->Release(); - delete d3dintf; - d3dintf = nullptr; - } } void renderer_d3d9::device_delete() @@ -990,11 +1053,7 @@ void renderer_d3d9::device_delete() m_texture_manager.reset(); // free the device itself - if (m_device != nullptr) - { - m_device->Release(); - m_device = nullptr; - } + m_device.Reset(); } @@ -1004,22 +1063,14 @@ void renderer_d3d9::device_delete() void renderer_d3d9::device_delete_resources() { - if (m_shaders != nullptr) - { + if (m_shaders) m_shaders->delete_resources(); - } - if (m_texture_manager != nullptr) - { + if (m_texture_manager) m_texture_manager->delete_resources(); - } // free the vertex buffer - if (m_vertexbuf != nullptr) - { - m_vertexbuf->Release(); - m_vertexbuf = nullptr; - } + m_vertexbuf.Reset(); } @@ -1032,7 +1083,7 @@ bool renderer_d3d9::device_verify_caps() bool success = true; D3DCAPS9 caps; - HRESULT result = d3dintf->d3dobj->GetDeviceCaps(m_adapter, D3DDEVTYPE_HAL, &caps); + HRESULT result = m_d3dobj->GetDeviceCaps(m_adapter, D3DDEVTYPE_HAL, &caps); if (FAILED(result)) { osd_printf_verbose("Direct3D: Error %08lX during GetDeviceCaps call\n", result); @@ -1042,7 +1093,7 @@ bool renderer_d3d9::device_verify_caps() if (caps.MaxPixelShader30InstructionSlots < 512) { osd_printf_verbose("Direct3D: Warning - Device does not support Pixel Shader 3.0, falling back to non-PS rendering\n"); - d3dintf->post_fx_available = false; + set_post_fx_unavailable(); } // verify presentation capabilities @@ -1097,7 +1148,7 @@ bool renderer_d3d9::device_verify_caps() } // verify texture formats - result = d3dintf->d3dobj->CheckDeviceFormat(m_adapter, D3DDEVTYPE_HAL, m_pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8); + result = m_d3dobj->CheckDeviceFormat(m_adapter, D3DDEVTYPE_HAL, m_pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8); if (FAILED(result)) { osd_printf_error("Direct3D Error: Your graphics card does not support the A8R8G8B8 texture format.\n"); @@ -1173,7 +1224,7 @@ int renderer_d3d9::config_adapter_mode() // get the identifier D3DADAPTER_IDENTIFIER9 id; - HRESULT result = d3dintf->d3dobj->GetAdapterIdentifier(m_adapter, 0, &id); + HRESULT result = m_d3dobj->GetAdapterIdentifier(m_adapter, 0, &id); if (FAILED(result)) { osd_printf_error("Error getting identifier for adapter #%d\n", m_adapter); @@ -1183,22 +1234,20 @@ int renderer_d3d9::config_adapter_mode() osd_printf_verbose("Direct3D: Configuring adapter #%d = %s\n", m_adapter, id.Description); // get the current display mode - result = d3dintf->d3dobj->GetAdapterDisplayMode(m_adapter, &m_origmode); + result = m_d3dobj->GetAdapterDisplayMode(m_adapter, &m_origmode); if (FAILED(result)) { osd_printf_error("Error getting mode for adapter #%d\n", m_adapter); return 1; } - auto win = assert_window(); - - // choose a resolution: window mode case - if (!win->fullscreen() || !video_config.switchres || win->win_has_menu()) + if (!window().fullscreen() || !video_config.switchres) { - RECT client; + // choose a resolution: window mode case // bounds are from the window client rect - GetClientRectExceptMenu(std::static_pointer_cast<win_window_info>(win)->platform_window(), &client, win->fullscreen()); + RECT client; + GetClientRectExceptMenu(dynamic_cast<win_window_info &>(window()).platform_window(), &client, window().fullscreen()); m_width = client.right - client.left; m_height = client.bottom - client.top; @@ -1209,14 +1258,14 @@ int renderer_d3d9::config_adapter_mode() // make sure it's a pixel format we can get behind if (m_pixformat != D3DFMT_X1R5G5B5 && m_pixformat != D3DFMT_R5G6B5 && m_pixformat != D3DFMT_X8R8G8B8) { - osd_printf_error("Device %s currently in an unsupported mode\n", win->monitor()->devicename()); + osd_printf_error("Device %s currently in an unsupported mode\n", window().monitor()->devicename()); return 1; } } - - // choose a resolution: full screen mode case else { + // choose a resolution: full screen mode case + // default to the current mode exactly m_width = m_origmode.Width; m_height = m_origmode.Height; @@ -1229,10 +1278,10 @@ int renderer_d3d9::config_adapter_mode() } // see if we can handle the device type - result = d3dintf->d3dobj->CheckDeviceType(m_adapter, D3DDEVTYPE_HAL, m_pixformat, m_pixformat, !win->fullscreen()); + result = m_d3dobj->CheckDeviceType(m_adapter, D3DDEVTYPE_HAL, m_pixformat, m_pixformat, !window().fullscreen()); if (FAILED(result)) { - osd_printf_error("Proposed video mode not supported on device %s\n", win->monitor()->devicename()); + osd_printf_error("Proposed video mode not supported on device %s\n", window().monitor()->devicename()); return 1; } @@ -1246,18 +1295,16 @@ int renderer_d3d9::config_adapter_mode() int renderer_d3d9::get_adapter_for_monitor() { - int maxadapter = d3dintf->d3dobj->GetAdapterCount(); - - auto win = assert_window(); + int maxadapter = m_d3dobj->GetAdapterCount(); // iterate over adapters until we error or find a match for (int adapternum = 0; adapternum < maxadapter; adapternum++) { // get the monitor for this adapter - HMONITOR curmonitor = d3dintf->d3dobj->GetAdapterMonitor(adapternum); + HMONITOR curmonitor = m_d3dobj->GetAdapterMonitor(adapternum); // if we match the proposed monitor, this is it - if (curmonitor == reinterpret_cast<HMONITOR>(win->monitor()->oshandle())) + if (curmonitor == reinterpret_cast<HMONITOR>(window().monitor()->oshandle())) { return adapternum; } @@ -1278,10 +1325,8 @@ void renderer_d3d9::pick_best_mode() int32_t minwidth, minheight; float best_score = 0.0f; - auto win = assert_window(); - // determine the refresh rate of the primary screen - const screen_device *primary_screen = screen_device_enumerator(win->machine().root_device()).first(); + const screen_device *primary_screen = screen_device_enumerator(window().machine().root_device()).first(); if (primary_screen != nullptr) { target_refresh = ATTOSECONDS_TO_HZ(primary_screen->refresh_attoseconds()); @@ -1291,14 +1336,14 @@ void renderer_d3d9::pick_best_mode() // note: technically we should not be calling this from an alternate window // thread; however, it is only done during init time, and the init code on // the main thread is waiting for us to finish, so it is safe to do so here - win->target()->compute_minimum_size(minwidth, minheight); + window().target()->compute_minimum_size(minwidth, minheight); // use those as the target for now int32_t target_width = minwidth; int32_t target_height = minheight; // determine the maximum number of modes - int maxmodes = d3dintf->d3dobj->GetAdapterModeCount(m_adapter, D3DFMT_X8R8G8B8); + int maxmodes = m_d3dobj->GetAdapterModeCount(m_adapter, D3DFMT_X8R8G8B8); // enumerate all the video modes and find the best match osd_printf_verbose("Direct3D: Selecting video mode...\n"); @@ -1306,7 +1351,7 @@ void renderer_d3d9::pick_best_mode() { // check this mode D3DDISPLAYMODE mode; - HRESULT result = d3dintf->d3dobj->EnumAdapterModes(m_adapter, D3DFMT_X8R8G8B8, modenum, &mode); + HRESULT result = m_d3dobj->EnumAdapterModes(m_adapter, D3DFMT_X8R8G8B8, modenum, &mode); if (FAILED(result)) break; @@ -1326,7 +1371,7 @@ void renderer_d3d9::pick_best_mode() size_score *= 0.1f; // if we're looking for a particular mode, that's a winner - if (mode.Width == win->m_win_config.width && mode.Height == win->m_win_config.height) + if (mode.Width == window().m_win_config.width && mode.Height == window().m_win_config.height) size_score = 2.0f; // compute refresh score @@ -1337,7 +1382,7 @@ void renderer_d3d9::pick_best_mode() refresh_score *= 0.1f; // if we're looking for a particular refresh, make sure it matches - if (mode.RefreshRate == win->m_win_config.refresh) + if (mode.RefreshRate == window().m_win_config.refresh) refresh_score = 2.0f; // weight size and refresh equally @@ -1364,33 +1409,32 @@ void renderer_d3d9::pick_best_mode() bool renderer_d3d9::update_window_size() { - auto win = assert_window(); - // get the current window bounds + auto &win = dynamic_cast<win_window_info &>(window()); RECT client; - GetClientRectExceptMenu(std::static_pointer_cast<win_window_info>(win)->platform_window(), &client, win->fullscreen()); + GetClientRectExceptMenu(win.platform_window(), &client, window().fullscreen()); // if we have a device and matching width/height, nothing to do - if (m_device != nullptr && rect_width(&client) == m_width && rect_height(&client) == m_height) + if (m_device && rect_width(&client) == m_width && rect_height(&client) == m_height) { // clear out any pending resizing if the area didn't change - if (win->m_resize_state == RESIZE_STATE_PENDING) - win->m_resize_state = RESIZE_STATE_NORMAL; + if (win.m_resize_state == win_window_info::RESIZE_STATE_PENDING) + win.m_resize_state = win_window_info::RESIZE_STATE_NORMAL; return false; } // if we're in the middle of resizing, leave it alone as well - if (win->m_resize_state == RESIZE_STATE_RESIZING) + if (win.m_resize_state == win_window_info::RESIZE_STATE_RESIZING) return false; // set the new bounds and create the device again m_width = rect_width(&client); m_height = rect_height(&client); - if (device_create(std::static_pointer_cast<win_window_info>(win->main_window())->platform_window())) + if (device_create(win.main_window()->platform_window())) return false; // reset the resize state to normal, and indicate we made a change - win->m_resize_state = RESIZE_STATE_NORMAL; + win.m_resize_state = win_window_info::RESIZE_STATE_NORMAL; return true; } @@ -1401,8 +1445,6 @@ bool renderer_d3d9::update_window_size() void renderer_d3d9::batch_vectors(int vector_count) { - auto win = assert_window(); - float quad_width = 0.0f; float quad_height = 0.0f; float target_width = 0.0f; @@ -1412,9 +1454,10 @@ void renderer_d3d9::batch_vectors(int vector_count) int triangle_count = vector_count * 2; m_vectorbatch = mesh_alloc(vertex_count); m_batchindex = 0; + uint32_t tint = 0xffffffff; uint32_t cached_flags = 0; - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { switch (prim.type) { @@ -1423,6 +1466,12 @@ void renderer_d3d9::batch_vectors(int vector_count) { batch_vector(prim); cached_flags = prim.flags; + + const uint8_t a = (uint8_t)std::round(prim.color.a * 255); + const uint8_t r = (uint8_t)std::round(prim.color.r * 255); + const uint8_t g = (uint8_t)std::round(prim.color.g * 255); + const uint8_t b = (uint8_t)std::round(prim.color.b * 255); + tint = (a << 24) | (b << 16) | (g << 8) | r; } break; @@ -1433,6 +1482,12 @@ void renderer_d3d9::batch_vectors(int vector_count) quad_height = prim.get_quad_height(); target_width = prim.get_full_quad_width(); target_height = prim.get_full_quad_height(); + + const uint8_t a = (uint8_t)std::round(prim.color.a * 255); + const uint8_t r = (uint8_t)std::round(prim.color.r * 255); + const uint8_t g = (uint8_t)std::round(prim.color.g * 255); + const uint8_t b = (uint8_t)std::round(prim.color.b * 255); + tint = (a << 24) | (b << 16) | (g << 8) | r; } break; @@ -1446,15 +1501,15 @@ void renderer_d3d9::batch_vectors(int vector_count) if (m_shaders->enabled()) { bool orientation_swap_xy = - (win->machine().system().flags & ORIENTATION_SWAP_XY) == ORIENTATION_SWAP_XY; + (window().machine().system().flags & ORIENTATION_SWAP_XY) == ORIENTATION_SWAP_XY; bool rotation_swap_xy = - (win->target()->orientation() & ORIENTATION_SWAP_XY) == ORIENTATION_SWAP_XY; + (window().target()->orientation() & ORIENTATION_SWAP_XY) == ORIENTATION_SWAP_XY; bool swap_xy = orientation_swap_xy ^ rotation_swap_xy; - bool rotation_0 = win->target()->orientation() == ROT0; - bool rotation_90 = win->target()->orientation() == ROT90; - bool rotation_180 = win->target()->orientation() == ROT180; - bool rotation_270 = win->target()->orientation() == ROT270; + bool rotation_0 = window().target()->orientation() == ROT0; + bool rotation_90 = window().target()->orientation() == ROT90; + bool rotation_180 = window().target()->orientation() == ROT180; + bool rotation_270 = window().target()->orientation() == ROT270; bool flip_x = ((rotation_0 || rotation_270) && orientation_swap_xy) || ((rotation_180 || rotation_270) && !orientation_swap_xy); @@ -1510,7 +1565,7 @@ void renderer_d3d9::batch_vectors(int vector_count) } // now add a polygon entry - m_poly[m_numpolys].init(D3DPT_TRIANGLELIST, triangle_count, vertex_count, cached_flags, nullptr, D3DTOP_MODULATE, quad_width, quad_height); + m_poly[m_numpolys].init(D3DPT_TRIANGLELIST, triangle_count, vertex_count, cached_flags, nullptr, D3DTOP_MODULATE, quad_width, quad_height, tint); m_numpolys++; } @@ -1652,10 +1707,10 @@ void renderer_d3d9::draw_line(const render_primitive &prim) vertex[3].v0 = stop.c.y; // determine the color of the line - auto r = (int32_t)(prim.color.r * 255.0f); - auto g = (int32_t)(prim.color.g * 255.0f); - auto b = (int32_t)(prim.color.b * 255.0f); - auto a = (int32_t)(prim.color.a * 255.0f); + auto r = (int32_t)std::round(prim.color.r * 255.0f); + auto g = (int32_t)std::round(prim.color.g * 255.0f); + auto b = (int32_t)std::round(prim.color.b * 255.0f); + auto a = (int32_t)std::round(prim.color.a * 255.0f); DWORD color = D3DCOLOR_ARGB(a, r, g, b); // set the color, Z parameters to standard values @@ -1667,7 +1722,7 @@ void renderer_d3d9::draw_line(const render_primitive &prim) } // now add a polygon entry - m_poly[m_numpolys].init(D3DPT_TRIANGLESTRIP, 2, 4, prim.flags, nullptr, D3DTOP_MODULATE, 0.0f, 0.0f); + m_poly[m_numpolys].init(D3DPT_TRIANGLESTRIP, 2, 4, prim.flags, nullptr, D3DTOP_MODULATE, 0.0f, 0.0f, (uint32_t)color); m_numpolys++; } @@ -1721,10 +1776,10 @@ void renderer_d3d9::draw_quad(const render_primitive &prim) } // determine the color, allowing for over modulation - auto r = (int32_t)(prim.color.r * 255.0f); - auto g = (int32_t)(prim.color.g * 255.0f); - auto b = (int32_t)(prim.color.b * 255.0f); - auto a = (int32_t)(prim.color.a * 255.0f); + auto r = (int32_t)std::round(prim.color.r * 255.0f); + auto g = (int32_t)std::round(prim.color.g * 255.0f); + auto b = (int32_t)std::round(prim.color.b * 255.0f); + auto a = (int32_t)std::round(prim.color.a * 255.0f); DWORD color = D3DCOLOR_ARGB(a, r, g, b); // adjust half pixel X/Y offset, set the color, Z parameters to standard values @@ -1738,7 +1793,7 @@ void renderer_d3d9::draw_quad(const render_primitive &prim) } // now add a polygon entry - m_poly[m_numpolys].init(D3DPT_TRIANGLESTRIP, 2, 4, prim.flags, texture, D3DTOP_MODULATE, quad_width, quad_height); + m_poly[m_numpolys].init(D3DPT_TRIANGLESTRIP, 2, 4, prim.flags, texture, D3DTOP_MODULATE, quad_width, quad_height, (uint32_t)color); m_numpolys++; } @@ -1798,7 +1853,7 @@ void renderer_d3d9::primitive_flush_pending() m_lockedbuf = nullptr; // set the stream - result = m_device->SetStreamSource(0, m_vertexbuf, 0, sizeof(vertex)); + result = m_device->SetStreamSource(0, m_vertexbuf.Get(), 0, sizeof(vertex)); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetStreamSource call\n", result); @@ -1826,9 +1881,17 @@ void renderer_d3d9::primitive_flush_pending() newfilter = FALSE; if (PRIMFLAG_GET_SCREENTEX(flags)) newfilter = video_config.filter; - set_filter(newfilter); - set_wrap(PRIMFLAG_GET_TEXWRAP(flags) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); - set_modmode(m_poly[polynum].modmode()); + + if (m_shaders->enabled()) + { + m_shaders->set_filter(newfilter); + } + else + { + set_filter(newfilter); + set_wrap(PRIMFLAG_GET_TEXWRAP(flags) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); + set_modmode(m_poly[polynum].modmode()); + } } if (vertnum + m_poly[polynum].numverts() > m_numverts) @@ -1841,9 +1904,6 @@ void renderer_d3d9::primitive_flush_pending() if(m_shaders->enabled()) { - // reset blend mode (handled by shader passes) - set_blendmode(BLENDMODE_NONE); - m_shaders->render_quad(&m_poly[polynum], vertnum); } else @@ -1896,19 +1956,6 @@ void renderer_d3d9::set_sliders_dirty() texture_info::~texture_info() { - if (m_d3dfinaltex != nullptr) - { - if (m_d3dtex == m_d3dfinaltex) - m_d3dtex = nullptr; - - m_d3dfinaltex->Release(); - } - - if (m_d3dtex != nullptr) - m_d3dtex->Release(); - - if (m_d3dsurface != nullptr) - m_d3dsurface->Release(); } @@ -1916,153 +1963,117 @@ texture_info::~texture_info() // texture_info constructor //============================================================ -texture_info::texture_info(d3d_texture_manager *manager, const render_texinfo* texsource, int prescale, uint32_t flags) +texture_info::texture_info(d3d_texture_manager &manager, const render_texinfo* texsource, int prescale, uint32_t flags) + : m_texture_manager(manager) + , m_renderer(manager.get_d3d()) + , m_hash(manager.texture_compute_hash(texsource, flags)) + , m_flags(flags) + , m_texinfo(*texsource) + , m_type(!PRIMFLAG_GET_SCREENTEX(flags) ? TEXTURE_TYPE_PLAIN : TEXTURE_TYPE_DYNAMIC) // required to compute texture size + , m_xprescale(PRIMFLAG_GET_SCREENTEX(flags) ? prescale : 1) + , m_yprescale(PRIMFLAG_GET_SCREENTEX(flags) ? prescale : 1) { - HRESULT result; - - // fill in the core data - m_texture_manager = manager; - m_renderer = m_texture_manager->get_d3d(); - m_hash = m_texture_manager->texture_compute_hash(texsource, flags); - m_flags = flags; - m_texinfo = *texsource; - m_xprescale = prescale; - m_yprescale = prescale; - - m_d3dtex = nullptr; - m_d3dsurface = nullptr; - m_d3dfinaltex = nullptr; - - // determine texture type, required to compute texture size - if (!PRIMFLAG_GET_SCREENTEX(flags)) - { - m_type = TEXTURE_TYPE_PLAIN; - } - else - { - m_type = TEXTURE_TYPE_DYNAMIC; - } - // compute the size compute_size(texsource->width, texsource->height); - // non-screen textures are easy if (!PRIMFLAG_GET_SCREENTEX(flags)) { + // non-screen textures are easy assert(PRIMFLAG_TEXFORMAT(flags) != TEXFORMAT_YUY16); - result = m_renderer->get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &m_d3dtex, nullptr); + const HRESULT result = m_renderer.get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &m_d3dtex, nullptr); if (FAILED(result)) goto error; + m_d3dfinaltex = m_d3dtex; } - - // screen textures are allocated differently else { - D3DFORMAT format; - DWORD usage = D3DUSAGE_DYNAMIC; - D3DPOOL pool = D3DPOOL_DEFAULT; - int maxdim = std::max(m_renderer->get_presentation()->BackBufferWidth, m_renderer->get_presentation()->BackBufferHeight); + // screen textures are allocated differently // pick the format + const DWORD usage = D3DUSAGE_DYNAMIC; + const D3DPOOL pool = D3DPOOL_DEFAULT; + D3DFORMAT format; if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_YUY16) - { - format = m_texture_manager->get_yuv_format(); - } + format = m_texture_manager.get_yuv_format(); else if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_ARGB32) - { format = D3DFMT_A8R8G8B8; - } else - { - format = m_renderer->get_screen_format(); - } + format = m_renderer.get_screen_format(); // don't prescale above screen size - while (m_xprescale > 1 && m_rawdims.c.x * m_xprescale >= 2 * maxdim) - { + const int maxdim = std::max( + m_renderer.get_presentation()->BackBufferWidth, + m_renderer.get_presentation()->BackBufferHeight); + + while ((m_xprescale > 1) && ((m_rawdims.c.x * m_xprescale) >= (2 * maxdim))) m_xprescale--; - } - while (m_xprescale > 1 && m_rawdims.c.x * m_xprescale > manager->get_max_texture_width()) - { + while ((m_xprescale > 1) && ((m_rawdims.c.x * m_xprescale) > manager.get_max_texture_width())) m_xprescale--; - } - while (m_yprescale > 1 && m_rawdims.c.y * m_yprescale >= 2 * maxdim) - { + + while ((m_yprescale > 1) && ((m_rawdims.c.y * m_yprescale) >= (2 * maxdim))) m_yprescale--; - } - while (m_yprescale > 1 && m_rawdims.c.y * m_yprescale > manager->get_max_texture_height()) - { + while ((m_yprescale > 1) && ((m_rawdims.c.y * m_yprescale) > manager.get_max_texture_height())) m_yprescale--; - } - - auto win = m_renderer->assert_window(); - int prescale = win->prescale(); - if (m_xprescale != prescale || m_yprescale != prescale) - { + const int prescale = m_renderer.window().prescale(); + if ((m_xprescale != prescale) || (m_yprescale != prescale)) osd_printf_verbose("Direct3D: adjusting prescale from %dx%d to %dx%d\n", prescale, prescale, m_xprescale, m_yprescale); - } // loop until we allocate something or error + HRESULT result; for (int attempt = 0; attempt < 2; attempt++) { - // second attempt is always 1:1 - if (attempt == 1) - { - m_xprescale = m_yprescale = 1; - } - - // screen textures with no prescaling are pretty easy - if (m_xprescale == 1 && m_yprescale == 1) + if ((m_xprescale == 1) && (m_yprescale == 1)) { - result = m_renderer->get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, usage, format, pool, &m_d3dtex, nullptr); + // screen textures with no prescaling are pretty easy + result = m_renderer.get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, usage, format, pool, &m_d3dtex, nullptr); if (result == D3D_OK) { m_d3dfinaltex = m_d3dtex; break; } } - // screen textures with prescaling require two allocations else { - result = m_renderer->get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, usage, format, pool, &m_d3dtex, nullptr); - if (FAILED(result)) + // screen textures with prescaling require two allocations + result = m_renderer.get_device()->CreateTexture(m_rawdims.c.x, m_rawdims.c.y, 1, usage, format, pool, &m_d3dtex, nullptr); + if (result == D3D_OK) { - continue; - } + // for the target surface, we allocate a render target texture + const int scwidth = m_rawdims.c.x * m_xprescale; + const int scheight = m_rawdims.c.y * m_yprescale; - // for the target surface, we allocate a render target texture - int scwidth = m_rawdims.c.x * m_xprescale; - int scheight = m_rawdims.c.y * m_yprescale; + // target surfaces typically cannot be YCbCr, so we always pick RGB in that case + const D3DFORMAT finalfmt = (format != m_texture_manager.get_yuv_format()) ? format : D3DFMT_A8R8G8B8; - // target surfaces typically cannot be YCbCr, so we always pick RGB in that case - D3DFORMAT finalfmt = (format != m_texture_manager->get_yuv_format()) ? format : D3DFMT_A8R8G8B8; + result = m_renderer.get_device()->CreateTexture(scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, finalfmt, D3DPOOL_DEFAULT, &m_d3dfinaltex, nullptr); + if (result == D3D_OK) + break; - result = m_renderer->get_device()->CreateTexture(scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, finalfmt, D3DPOOL_DEFAULT, &m_d3dfinaltex, nullptr); - if (result == D3D_OK) - { - break; + m_d3dtex.Reset(); } - - m_d3dtex->Release(); - m_d3dtex = nullptr; } + + // second attempt is always 1:1 + m_xprescale = m_yprescale = 1; } + if (FAILED(result)) + goto error; } // copy the data to the texture + assert(m_d3dtex); + assert(m_d3dfinaltex); set_data(texsource, flags); return; error: - d3dintf->post_fx_available = false; + m_renderer.set_post_fx_unavailable(); osd_printf_error("Direct3D: Critical warning: A texture failed to allocate. Expect things to get bad quickly.\n"); - if (m_d3dsurface != nullptr) - m_d3dsurface->Release(); - if (m_d3dtex != nullptr) - m_d3dtex->Release(); + m_d3dsurface.Reset(); + m_d3dtex.Reset(); } @@ -2076,11 +2087,11 @@ void texture_info::compute_size_subroutine(int texwidth, int texheight, int* p_w int finalwidth = texwidth; // adjust the aspect ratio if we need to - while (finalwidth < finalheight && finalheight / finalwidth > m_texture_manager->get_max_texture_aspect()) + while (finalwidth < finalheight && finalheight / finalwidth > m_texture_manager.get_max_texture_aspect()) { finalwidth *= 2; } - while (finalheight < finalwidth && finalwidth / finalheight > m_texture_manager->get_max_texture_aspect()) + while (finalheight < finalwidth && finalwidth / finalheight > m_texture_manager.get_max_texture_aspect()) { finalheight *= 2; } @@ -2102,7 +2113,7 @@ void texture_info::compute_size(int texwidth, int texheight) m_xborderpix = 0; m_yborderpix = 0; - bool shaders_enabled = m_renderer->get_shaders()->enabled(); + bool shaders_enabled = m_renderer.get_shaders()->enabled(); bool wrap_texture = (m_flags & PRIMFLAG_TEXWRAP_MASK) == PRIMFLAG_TEXWRAP_MASK; // skip border when shaders are enabled @@ -2126,7 +2137,7 @@ void texture_info::compute_size(int texwidth, int texheight) compute_size_subroutine(finalwidth, finalheight, &finalwidth, &finalheight); // if we added pixels for the border, and that just barely pushed us over, take it back - if (finalwidth > m_texture_manager->get_max_texture_width() || finalheight > m_texture_manager->get_max_texture_height()) + if (finalwidth > m_texture_manager.get_max_texture_width() || finalheight > m_texture_manager.get_max_texture_height()) { finalheight = texheight; finalwidth = texwidth; @@ -2139,10 +2150,18 @@ void texture_info::compute_size(int texwidth, int texheight) } // if we're above the max width/height, do what? - if (finalwidth > m_texture_manager->get_max_texture_width() || finalheight > m_texture_manager->get_max_texture_height()) + if (finalwidth > m_texture_manager.get_max_texture_width() || finalheight > m_texture_manager.get_max_texture_height()) { static bool printed = false; - if (!printed) osd_printf_warning("Texture too big! (wanted: %dx%d, max is %dx%d)\n", finalwidth, finalheight, (int)m_texture_manager->get_max_texture_width(), (int)m_texture_manager->get_max_texture_height()); + if (!printed) + { + osd_printf_warning( + "Texture too big! (wanted: %dx%d, max is %dx%d)\n", + finalwidth, + finalheight, + m_texture_manager.get_max_texture_width(), + m_texture_manager.get_max_texture_height()); + } printed = true; } @@ -2395,9 +2414,9 @@ void texture_info::set_data(const render_texinfo *texsource, uint32_t flags) break; case TEXFORMAT_YUY16: - if (m_texture_manager->get_yuv_format() == D3DFMT_YUY2) + if (m_texture_manager.get_yuv_format() == D3DFMT_YUY2) copyline_yuy16_to_yuy2((uint16_t *)dst, (uint16_t *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette); - else if (m_texture_manager->get_yuv_format() == D3DFMT_UYVY) + else if (m_texture_manager.get_yuv_format() == D3DFMT_UYVY) copyline_yuy16_to_uyvy((uint16_t *)dst, (uint16_t *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette); else copyline_yuy16_to_argb((uint32_t *)dst, (uint16_t *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette); @@ -2432,48 +2451,51 @@ void texture_info::set_data(const render_texinfo *texsource, uint32_t flags) void texture_info::prescale() { - IDirect3DSurface9 *scale_surface; - HRESULT result; - int i; - // if we don't need to, just skip it - if (m_d3dtex == m_d3dfinaltex) + if (m_d3dtex.Get() == m_d3dfinaltex.Get()) + { + assert(m_xprescale == 1); + assert(m_yprescale == 1); return; + } + + assert(m_d3dtex); + assert((m_xprescale > 1) || (m_yprescale > 1)); + HRESULT result; // for all cases, we need to get the surface of the render target + Microsoft::WRL::ComPtr<IDirect3DSurface9> scale_surface; result = m_d3dfinaltex->GetSurfaceLevel(0, &scale_surface); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during texture GetSurfaceLevel call\n", result); // if we have an offscreen plain surface, we can just StretchRect to it - IDirect3DSurface9 *backbuffer; - - assert(m_d3dtex != nullptr); // first remember the original render target and set the new one - result = m_renderer->get_device()->GetRenderTarget(0, &backbuffer); + Microsoft::WRL::ComPtr<IDirect3DSurface9> backbuffer; + result = m_renderer.get_device()->GetRenderTarget(0, &backbuffer); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device GetRenderTarget call\n", result); - result = m_renderer->get_device()->SetRenderTarget(0, scale_surface); + result = m_renderer.get_device()->SetRenderTarget(0, scale_surface.Get()); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call 1\n", result); - m_renderer->reset_render_states(); + m_renderer.reset_render_states(); // start the scene - result = m_renderer->get_device()->BeginScene(); + result = m_renderer.get_device()->BeginScene(); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device BeginScene call\n", result); // configure the rendering pipeline - m_renderer->set_filter(FALSE); - m_renderer->set_blendmode(BLENDMODE_NONE); - result = m_renderer->get_device()->SetTexture(0, m_d3dtex); + m_renderer.set_filter(FALSE); + m_renderer.set_blendmode(BLENDMODE_NONE); + result = m_renderer.get_device()->SetTexture(0, m_d3dtex.Get()); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetTexture call\n", result); // lock the vertex buffer vertex *lockedbuf; - result = m_renderer->get_vertex_buffer()->Lock(0, 0, (VOID **)&lockedbuf, D3DLOCK_DISCARD); + result = m_renderer.get_vertex_buffer()->Lock(0, 0, (VOID **)&lockedbuf, D3DLOCK_DISCARD); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during vertex buffer lock call\n", result); @@ -2498,7 +2520,7 @@ void texture_info::prescale() lockedbuf[3].v0 = (float)(m_texinfo.height + 2 * m_yborderpix) / (float)m_rawdims.c.y; // reset the remaining vertex parameters - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) { lockedbuf[i].z = 0.0f; lockedbuf[i].rhw = 1.0f; @@ -2506,32 +2528,32 @@ void texture_info::prescale() } // unlock the vertex buffer - result = m_renderer->get_vertex_buffer()->Unlock(); + result = m_renderer.get_vertex_buffer()->Unlock(); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during vertex buffer unlock call\n", result); // set the stream and draw the triangle strip - result = m_renderer->get_device()->SetStreamSource(0, m_renderer->get_vertex_buffer(), 0, sizeof(vertex)); + result = m_renderer.get_device()->SetStreamSource(0, m_renderer.get_vertex_buffer(), 0, sizeof(vertex)); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetStreamSource call\n", result); - result = m_renderer->get_device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + result = m_renderer.get_device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device DrawPrimitive call\n", result); // end the scene - result = m_renderer->get_device()->EndScene(); + result = m_renderer.get_device()->EndScene(); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device end_scene call\n", result); // reset the render target and release our reference to the backbuffer - result = m_renderer->get_device()->SetRenderTarget(0, backbuffer); + result = m_renderer.get_device()->SetRenderTarget(0, backbuffer.Get()); if (FAILED(result)) osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call 2\n", result); - backbuffer->Release(); - m_renderer->reset_render_states(); + backbuffer.Reset(); + m_renderer.reset_render_states(); // release our reference to the target surface - scale_surface->Release(); + scale_surface.Reset(); } @@ -2541,35 +2563,6 @@ void texture_info::prescale() d3d_render_target::~d3d_render_target() { - for (int index = 0; index < MAX_BLOOM_COUNT; index++) - { - if (bloom_texture[index] != nullptr) - bloom_texture[index]->Release(); - - if (bloom_surface[index] != nullptr) - bloom_surface[index]->Release(); - } - - for (int index = 0; index < 2; index++) - { - if (source_texture[index] != nullptr) - source_texture[index]->Release(); - - if (source_surface[index] != nullptr) - source_surface[index]->Release(); - - if (target_texture[index] != nullptr) - target_texture[index]->Release(); - - if (target_surface[index] != nullptr) - target_surface[index]->Release(); - } - - if (cache_texture != nullptr) - cache_texture->Release(); - - if (cache_surface != nullptr) - cache_surface->Release(); } @@ -2610,9 +2603,7 @@ bool d3d_render_target::init(renderer_d3d9 *d3d, int source_width, int source_he cache_texture->GetSurfaceLevel(0, &cache_surface); - auto win = d3d->assert_window(); - - const screen_device *first_screen = screen_device_enumerator(win->machine().root_device()).first(); + const screen_device *first_screen = screen_device_enumerator(d3d->window().machine().root_device()).first(); bool vector_screen = first_screen != nullptr && first_screen->screen_type() == SCREEN_TYPE_VECTOR; diff --git a/src/osd/modules/render/drawd3d.h b/src/osd/modules/render/drawd3d.h index 2fa4a30873c..3ab7651d87c 100644 --- a/src/osd/modules/render/drawd3d.h +++ b/src/osd/modules/render/drawd3d.h @@ -10,20 +10,24 @@ #pragma once +#include "d3d/d3dcomm.h" -#ifdef OSD_WINDOWS +#include "modules/lib/osdlib.h" +#include "modules/osdwindow.h" + +#include "sliderdirtynotifier.h" #include <windows.h> #include <tchar.h> #include <mmsystem.h> #include <d3d9.h> #include <d3dx9.h> -#include <cmath> #undef interface -#include "d3d/d3dcomm.h" -#include "sliderdirtynotifier.h" -#include "modules/lib/osdlib.h" +#include <memory> +#include <vector> +#include <cmath> + //============================================================ // CONSTANTS @@ -36,15 +40,6 @@ // TYPE DEFINITIONS //============================================================ -struct d3d_base -{ - // internal objects - IDirect3D9 *d3dobj; - bool post_fx_available; - - osd::dynamic_module::ptr d3d9_dll; -}; - class shaders; struct hlsl_options; @@ -52,11 +47,10 @@ struct hlsl_options; class renderer_d3d9 : public osd_renderer, public slider_dirty_notifier { public: - renderer_d3d9(std::shared_ptr<osd_window> window); - virtual ~renderer_d3d9(); + using IDirect3D9Ptr = Microsoft::WRL::ComPtr<IDirect3D9>; - static bool init(running_machine &machine); - static void exit(); + renderer_d3d9(osd_window &window, const IDirect3D9Ptr &d3dobj); + virtual ~renderer_d3d9(); virtual int create() override; virtual render_primitive_list *get_primitives() override; @@ -113,11 +107,13 @@ public: vec2f get_dims() const { return vec2f(m_width, m_height); } int get_height() const { return m_height; } int get_refresh() const { return m_refresh; } + bool post_fx_available() const { return m_post_fx_available; } + void set_post_fx_unavailable() { m_post_fx_available = false; } - IDirect3DDevice9 * get_device() const { return m_device; } + IDirect3DDevice9 * get_device() const { return m_device.Get(); } D3DPRESENT_PARAMETERS * get_presentation() { return &m_presentation; } - IDirect3DVertexBuffer9 *get_vertex_buffer() const { return m_vertexbuf; } + IDirect3DVertexBuffer9 *get_vertex_buffer() const { return m_vertexbuf.Get(); } void set_toggle(bool toggle) { m_toggle = toggle; } @@ -125,27 +121,32 @@ public: D3DFORMAT get_pixel_format() const { return m_pixformat; } D3DDISPLAYMODE get_origmode() const { return m_origmode; } - uint32_t get_last_texture_flags() const { return m_last_texture_flags; } + uint32_t get_last_texture_flags() const { return m_last_texture_flags; } d3d_texture_manager * get_texture_manager() const { return m_texture_manager.get(); } texture_info * get_default_texture(); - shaders * get_shaders() const { return m_shaders; } + shaders * get_shaders() const { return m_shaders.get(); } private: + using IDirect3DDevice9Ptr = Microsoft::WRL::ComPtr<IDirect3DDevice9>; + using IDirect3DVertexBuffer9Ptr = Microsoft::WRL::ComPtr<IDirect3DVertexBuffer9>; + + const IDirect3D9Ptr m_d3dobj; // Direct3D 9 API object int m_adapter; // ordinal adapter number int m_width; // current width int m_height; // current height int m_refresh; // current refresh rate int m_create_error_count; // number of consecutive create errors + bool m_post_fx_available; - IDirect3DDevice9 * m_device; // pointer to the Direct3DDevice object + IDirect3DDevice9Ptr m_device; // pointer to the Direct3DDevice object int m_gamma_supported; // is full screen gamma supported? D3DPRESENT_PARAMETERS m_presentation; // set of presentation parameters D3DDISPLAYMODE m_origmode; // original display mode for the adapter D3DFORMAT m_pixformat; // pixel format we are using - IDirect3DVertexBuffer9 *m_vertexbuf; // pointer to the vertex buffer object + IDirect3DVertexBuffer9Ptr m_vertexbuf; // pointer to the vertex buffer object vertex * m_lockedbuf; // pointer to the locked vertex buffer int m_numverts; // number of accumulated vertices @@ -160,20 +161,18 @@ private: D3DFORMAT m_screen_format; // format to use for screen textures texture_info * m_last_texture; // previous texture - uint32_t m_last_texture_flags; // previous texture flags + uint32_t m_last_texture_flags; // previous texture flags int m_last_blendenable; // previous blendmode int m_last_blendop; // previous blendmode int m_last_blendsrc; // previous blendmode int m_last_blenddst; // previous blendmode int m_last_filter; // previous texture filter - uint32_t m_last_wrap; // previous wrap state + uint32_t m_last_wrap; // previous wrap state int m_last_modmode; // previous texture modulation - shaders * m_shaders; // HLSL interface + std::unique_ptr<shaders> m_shaders; // HLSL interface std::unique_ptr<d3d_texture_manager> m_texture_manager; // texture manager }; -#endif // OSD_WINDOWS - #endif // MAME_OSD_MODULES_RENDER_DRAWD3D_H diff --git a/src/osd/modules/render/drawgdi.cpp b/src/osd/modules/render/drawgdi.cpp index 32dac94c24a..f922ca24e8f 100644 --- a/src/osd/modules/render/drawgdi.cpp +++ b/src/osd/modules/render/drawgdi.cpp @@ -2,21 +2,53 @@ // copyright-holders:Aaron Giles //============================================================ // -// drawgdi.c - Win32 GDI drawing +// drawgdi.cpp - Win32 GDI drawing // //============================================================ +#include "render_module.h" + +#include "modules/osdmodule.h" + +#if defined(OSD_WINDOWS) + +#include "window.h" + +// emu #include "emu.h" -#include "drawgdi.h" #include "rendersw.hxx" -//============================================================ -// destructor -//============================================================ +// standard windows headers +#include <windows.h> + + +namespace osd { + +namespace { -renderer_gdi::~renderer_gdi() +// renderer_gdi is the information for the current screen +class renderer_gdi : public osd_renderer { -} +public: + renderer_gdi(osd_window &window) + : osd_renderer(window) + , m_bmdata(nullptr) + , m_bmsize(0) + { + } + + virtual int create() override; + virtual render_primitive_list *get_primitives() override; + virtual int draw(const int update) override; + virtual void save() override {} + virtual void record() override {} + virtual void toggle_fsfx() override {} + +private: + BITMAPINFO m_bminfo; + std::unique_ptr<uint8_t []> m_bmdata; + size_t m_bmsize; +}; //============================================================ // renderer_gdi::create @@ -34,6 +66,7 @@ int renderer_gdi::create() m_bminfo.bmiHeader.biYPelsPerMeter = 0; m_bminfo.bmiHeader.biClrUsed = 0; m_bminfo.bmiHeader.biClrImportant = 0; + return 0; } @@ -43,16 +76,12 @@ int renderer_gdi::create() render_primitive_list *renderer_gdi::get_primitives() { - auto win = try_getwindow(); - if (win == nullptr) + osd_dim const dimensions = window().get_size(); + if ((dimensions.width() <= 0) || (dimensions.height() <= 0)) return nullptr; - RECT client; - GetClientRect(std::static_pointer_cast<win_window_info>(win)->platform_window(), &client); - if ((rect_width(&client) == 0) || (rect_height(&client) == 0)) - return nullptr; - win->target()->set_bounds(rect_width(&client), rect_height(&client), win->pixel_aspect()); - return &win->target()->get_primitives(); + window().target()->set_bounds(dimensions.width(), dimensions.height(), window().pixel_aspect()); + return &window().target()->get_primitives(); } //============================================================ @@ -61,23 +90,24 @@ render_primitive_list *renderer_gdi::get_primitives() int renderer_gdi::draw(const int update) { - auto win = assert_window(); + auto &win = dynamic_cast<win_window_info &>(window()); // we don't have any special resize behaviors - if (win->m_resize_state == RESIZE_STATE_PENDING) - win->m_resize_state = RESIZE_STATE_NORMAL; + if (win.m_resize_state == win_window_info::RESIZE_STATE_PENDING) + win.m_resize_state = win_window_info::RESIZE_STATE_NORMAL; // get the target bounds RECT bounds; - GetClientRect(std::static_pointer_cast<win_window_info>(win)->platform_window(), &bounds); + GetClientRect(win.platform_window(), &bounds); // compute width/height/pitch of target - int width = rect_width(&bounds); - int height = rect_height(&bounds); - int pitch = (width + 3) & ~3; + osd_dim const dimensions = win.get_size(); + int const width = dimensions.width(); + int const height = dimensions.height(); + int const pitch = (width + 3) & ~3; // make sure our temporary bitmap is big enough - if (pitch * height * 4 > m_bmsize) + if ((pitch * height * 4) > m_bmsize) { m_bmsize = pitch * height * 4 * 2; m_bmdata.reset(); @@ -85,9 +115,9 @@ int renderer_gdi::draw(const int update) } // draw the primitives to the bitmap - win->m_primlist->acquire_lock(); - software_renderer<uint32_t, 0,0,0, 16,8,0>::draw_primitives(*win->m_primlist, m_bmdata.get(), width, height, pitch); - win->m_primlist->release_lock(); + win.m_primlist->acquire_lock(); + software_renderer<uint32_t, 0,0,0, 16,8,0>::draw_primitives(*win.m_primlist, m_bmdata.get(), width, height, pitch); + win.m_primlist->release_lock(); // fill in bitmap-specific info m_bminfo.bmiHeader.biWidth = pitch; @@ -95,8 +125,43 @@ int renderer_gdi::draw(const int update) // blit to the screen StretchDIBits( - win->m_dc, 0, 0, width, height, + win.m_dc, 0, 0, width, height, 0, 0, width, height, m_bmdata.get(), &m_bminfo, DIB_RGB_COLORS, SRCCOPY); + return 0; } + + +class video_gdi : public osd_module, public render_module +{ +public: + video_gdi() : osd_module(OSD_RENDERER_PROVIDER, "gdi") { } + + virtual int init(osd_interface &osd, osd_options const &options) override { return 0; } + virtual void exit() override { } + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return FLAG_INTERACTIVE; } +}; + +std::unique_ptr<osd_renderer> video_gdi::create(osd_window &window) +{ + return std::make_unique<renderer_gdi>(window); +} + +} // anonymous namespace + +} // namespace osd + + +#else // defined(OSD_WINDOWS) + +namespace osd { namespace { MODULE_NOT_SUPPORTED(video_gdi, OSD_RENDERER_PROVIDER, "gdi") } } + +#endif // defined(OSD_WINDOWS) + + +MODULE_DEFINITION(RENDERER_GDI, osd::video_gdi) diff --git a/src/osd/modules/render/drawgdi.h b/src/osd/modules/render/drawgdi.h deleted file mode 100644 index 0dfc4bf2641..00000000000 --- a/src/osd/modules/render/drawgdi.h +++ /dev/null @@ -1,55 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -//============================================================ -// -// drawgdi.h - Win32 GDI drawing -// -//============================================================ -#ifndef MAME_OSD_MODULES_RENDER_DRAWGDI_H -#define MAME_OSD_MODULES_RENDER_DRAWGDI_H - -#pragma once - - -// standard windows headers -#include <windows.h> - -// MAME headers - -// MAMEOS headers -#include "window.h" - - -//============================================================ -// TYPE DEFINITIONS -//============================================================ - -/* renderer_gdi is the information for the current screen */ -class renderer_gdi : public osd_renderer -{ -public: - renderer_gdi(std::shared_ptr<osd_window> window) - : osd_renderer(window, FLAG_NONE) - , m_bmdata(nullptr) - , m_bmsize(0) - { - } - virtual ~renderer_gdi(); - - static bool init(running_machine &machine) { return false; } - static void exit() { } - - virtual int create() override; - virtual render_primitive_list *get_primitives() override; - virtual int draw(const int update) override; - virtual void save() override {} - virtual void record() override {} - virtual void toggle_fsfx() override {} - -private: - BITMAPINFO m_bminfo; - std::unique_ptr<uint8_t []> m_bmdata; - size_t m_bmsize; -}; - -#endif // MAME_OSD_MODULES_RENDER_DRAWGDI_H diff --git a/src/osd/modules/render/drawnone.cpp b/src/osd/modules/render/drawnone.cpp index 6f591879038..55ce1bbed18 100644 --- a/src/osd/modules/render/drawnone.cpp +++ b/src/osd/modules/render/drawnone.cpp @@ -6,25 +6,76 @@ // //============================================================ -// standard windows headers -#include <windows.h> +#include "render_module.h" -#include "drawnone.h" +#include "modules/lib/osdobj_common.h" +#include "modules/osdmodule.h" +#include "modules/osdwindow.h" -//============================================================ -// drawnone_window_get_primitives -//============================================================ +#include "main.h" +#include "render.h" + +#include <memory> + + +namespace osd { + +namespace { + +class renderer_none : public osd_renderer +{ +public: + renderer_none(osd_window &window) : osd_renderer(window) { } + + virtual int create() override { return 0; } + virtual render_primitive_list *get_primitives() override; + virtual int draw(const int update) override { return 0; } + virtual void save() override { } + virtual void record() override { } + virtual void toggle_fsfx() override { } +}; render_primitive_list *renderer_none::get_primitives() { - auto win = try_getwindow(); - if (win == nullptr) + osd_dim const dimensions = window().get_size(); + if ((dimensions.width() <= 0) || (dimensions.height() <= 0)) return nullptr; - RECT client; - GetClientRect(std::static_pointer_cast<win_window_info>(win)->platform_window(), &client); - if ((rect_width(&client) == 0) || (rect_height(&client) == 0)) - return nullptr; - win->target()->set_bounds(rect_width(&client), rect_height(&client), win->pixel_aspect()); - return &win->target()->get_primitives(); + window().target()->set_bounds(dimensions.width(), dimensions.height(), window().pixel_aspect()); + return &window().target()->get_primitives(); } + + +class video_none : public osd_module, public render_module +{ +public: + video_none() : osd_module(OSD_RENDERER_PROVIDER, "none") { } + + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override { } + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return 0; } +}; + +int video_none::init(osd_interface &osd, osd_options const &options) +{ + if (!emulator_info::standalone() && (options.seconds_to_run() == 0)) + osd_printf_warning("Warning: -video none doesn't make much sense without -seconds_to_run\n"); + + return 0; +} + +std::unique_ptr<osd_renderer> video_none::create(osd_window &window) +{ + return std::make_unique<renderer_none>(window); +} + +} // anonymous namespace + +} // namespace osd + + +MODULE_DEFINITION(RENDERER_NONE, osd::video_none) diff --git a/src/osd/modules/render/drawnone.h b/src/osd/modules/render/drawnone.h deleted file mode 100644 index c884f969204..00000000000 --- a/src/osd/modules/render/drawnone.h +++ /dev/null @@ -1,33 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Aaron Giles -//============================================================ -// -// drawnone.h - stub "nothing" drawer -// -//============================================================ - -#ifndef __DRAWNONE__ -#define __DRAWNONE__ - -#include "window.h" - -class renderer_none : public osd_renderer -{ -public: - renderer_none(std::shared_ptr<osd_window> window) - : osd_renderer(window, FLAG_NONE) { } - - virtual ~renderer_none() { } - - static bool init(running_machine &machine) { return false; } - static void exit() { } - - virtual int create() override { return 0; } - virtual render_primitive_list *get_primitives() override; - virtual int draw(const int update) override { return 0; } - virtual void save() override { } - virtual void record() override { } - virtual void toggle_fsfx() override { } -}; - -#endif // __DRAWNONE__ diff --git a/src/osd/modules/render/drawogl.cpp b/src/osd/modules/render/drawogl.cpp index 9e200f3817c..f9f76946bf4 100644 --- a/src/osd/modules/render/drawogl.cpp +++ b/src/osd/modules/render/drawogl.cpp @@ -12,37 +12,60 @@ // //============================================================ -// standard C headers -#include <cmath> -#include <cstdio> +#include "render_module.h" + +#include "modules/osdmodule.h" + +#if USE_OPENGL -// MAME headers +// will be picked up from specific OSD implementation +#include "window.h" + +// OSD common headers +#include "modules/lib/osdlib.h" +#include "modules/lib/osdobj_common.h" +#include "modules/opengl/gl_shader_mgr.h" #include "osdcomm.h" -#include "emu.h" -#include "emuopts.h" -#ifdef OSD_MAC +// OSD headers +#if defined(OSD_WINDOWS) +typedef uint64_t HashT; +#include "winglcontext.h" +#elif defined(OSD_MAC) #define GL_SILENCE_DEPRECATION (1) +#include "osdmac.h" +#else +#include "osdsdl.h" +#include "sdlglcontext.h" #endif +// emu +#include "emucore.h" +#include "emuopts.h" +#include "render.h" + + #if !defined(OSD_WINDOWS) && !defined(OSD_MAC) + // standard SDL headers #define TOBEMIGRATED 1 #include <SDL2/SDL.h> -#endif -#include "modules/lib/osdlib.h" -#include "modules/lib/osdobj_common.h" +#endif // !defined(OSD_WINDOWS && !defined(OSD_MAC) -// OpenGL headers -#include "modules/opengl/osd_opengl.h" -#include "modules/opengl/gl_shader_tool.h" -#include "modules/opengl/gl_shader_mgr.h" +// standard C headers +#include <cmath> +#include <cstdio> +#include <memory> +#include <utility> + #if defined(SDLMAME_MACOSX) || defined(OSD_MAC) + #include <cstring> #include <cstdio> + #include <sys/types.h> #include <sys/sysctl.h> @@ -72,7 +95,13 @@ typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum m_target, GLuint r typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum m_target, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum m_target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); -#endif + +#endif // defined(SDLMAME_MACOSX) || defined(OSD_MAC) + + +namespace osd { + +namespace { // make sure the extensions compile OK everywhere #ifndef GL_TEXTURE_STORAGE_HINT_APPLE @@ -128,7 +157,283 @@ typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuin #define GL_DEPTH_COMPONENT32 0x81A7 #endif -#include "drawogl.h" + +//============================================================ +// Configuration +//============================================================ + +#define GLSL_SHADER_MAX 10 + +struct ogl_video_config +{ + ogl_video_config() = default; + + bool vbo = false; + bool pbo = false; + bool allowtexturerect = false; // allow GL_ARB_texture_rectangle, default: no + bool forcepow2texture = false; // force power of two textures, default: no + bool glsl = false; + int glsl_filter = 0; // glsl filtering, >0 disables filter + std::string glsl_shader_mamebm[GLSL_SHADER_MAX]; // custom glsl shader set, mame bitmap + int glsl_shader_mamebm_num = 0; // custom glsl shader set number, mame bitmap + std::string glsl_shader_scrn[GLSL_SHADER_MAX]; // custom glsl shader set, screen bitmap + int glsl_shader_scrn_num = 0; // custom glsl shader number, screen bitmap +}; + + +//============================================================ +// Textures +//============================================================ + +/* ogl_texture_info holds information about a texture */ +class ogl_texture_info +{ +public: + ogl_texture_info( +#if defined(USE_DISPATCH_GL) + osd_gl_dispatch *gld +#endif + ) +#if defined(USE_DISPATCH_GL) + : gl_dispatch(gld) +#endif + { + for (int i=0; i<2; i++) + { + mpass_textureunit[i] = 0; + mpass_texture_mamebm[i] = 0; + mpass_fbo_mamebm[i] = 0; + mpass_texture_scrn[i] = 0; + mpass_fbo_scrn[i] = 0; + } + for (int i=0; i<8; i++) + texCoord[i] = 0.0f; + } + +#if defined(USE_DISPATCH_GL) + osd_gl_dispatch *const gl_dispatch; // name is magic, can't be changed +#endif + HashT hash = 0; // hash value for the texture (must be >= pointer size) + uint32_t flags = 0; // rendering flags + render_texinfo texinfo; // copy of the texture info + int rawwidth = 0, rawheight = 0; // raw width/height of the texture + int rawwidth_create = 0; // raw width/height, pow2 compatible, if needed + int rawheight_create = 0; // (create and initial set the texture, not for copy!) + int type = 0; // what type of texture are we? + int format = 0; // texture format + int borderpix = 0; // do we have a 1 pixel border? + int xprescale = 0; // what is our X prescale factor? + int yprescale = 0; // what is our Y prescale factor? + int nocopy = 0; // must the texture date be copied? + + uint32_t texture = 0; // OpenGL texture "name"/ID + + GLenum texTarget = 0; // OpenGL texture target + int texpow2 = 0; // Is this texture pow2 + + uint32_t mpass_dest_idx = 0; // Multipass dest idx [0..1] + uint32_t mpass_textureunit[2]; // texture unit names for GLSL + + uint32_t mpass_texture_mamebm[2]; // Multipass OpenGL texture "name"/ID for the shader + uint32_t mpass_fbo_mamebm[2]; // framebuffer object for this texture, multipass + uint32_t mpass_texture_scrn[2]; // Multipass OpenGL texture "name"/ID for the shader + uint32_t mpass_fbo_scrn[2]; // framebuffer object for this texture, multipass + + uint32_t pbo = 0; // pixel buffer object for this texture (DYNAMIC only!) + uint32_t *data = nullptr; // pixels for the texture + bool data_own = false; // do we own / allocated it ? + GLfloat texCoord[8]; + GLuint texCoordBufferName; + +}; + +// renderer_ogl is the information about OpenGL for the current screen +class renderer_ogl : public osd_renderer +{ +public: + renderer_ogl(osd_window &window, ogl_video_config const &ogl_config) + : osd_renderer(window) + , m_ogl_config(ogl_config) + , m_blittimer(0) + , m_width(0) + , m_height(0) + , m_blit_dim(0, 0) + , m_initialized(0) + , m_last_blendmode(0) + , m_texture_max_width(0) + , m_texture_max_height(0) + , m_texpoweroftwo(0) + , m_usevbo(0) + , m_usepbo(0) + , m_usefbo(0) + , m_useglsl(0) + , m_glsl_program_num(0) + , m_glsl_program_mb2sc(0) + , m_usetexturerect(0) + , m_init_context(0) + , m_last_hofs(0.0f) + , m_last_vofs(0.0f) + , m_surf_w(0) + , m_surf_h(0) + { + for (int i=0; i < HASH_SIZE + OVERFLOW_SIZE; i++) + m_texhash[i] = nullptr; + for (int i=0; i < 2*GLSL_SHADER_MAX; i++) + m_glsl_program[i] = 0; + for (int i=0; i < 8; i++) + m_texVerticex[i] = 0.0f; + } + virtual ~renderer_ogl() + { + // free the memory in the window + destroy_all_textures(); + } + + virtual int create() override; + virtual int draw(const int update) override; + +#ifndef OSD_WINDOWS + virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; +#endif + virtual render_primitive_list *get_primitives() override + { + osd_dim nd = window().get_size(); + if (nd != m_blit_dim) + { + m_blit_dim = nd; + notify_changed(); + } + if ((m_blit_dim.width() == 0) || (m_blit_dim.height() == 0)) + return nullptr; + window().target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), window().pixel_aspect()); + return &window().target()->get_primitives(); + } + +#ifdef OSD_WINDOWS + virtual void save() override { } + virtual void record() override { } + virtual void toggle_fsfx() override { } +#endif + +private: + static const uint32_t HASH_SIZE = ((1 << 18) + 1); + static const uint32_t OVERFLOW_SIZE = (1 << 12); + + void destroy_all_textures(); + + void loadGLExtensions(); + void loadgl_functions(); + void initialize_gl(); + void set_blendmode(int blendmode); + HashT texture_compute_hash(const render_texinfo *texture, uint32_t flags); + void texture_compute_type_subroutine(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); + void texture_compute_size_subroutine(ogl_texture_info *texture, uint32_t flags, + uint32_t width, uint32_t height, + int* p_width, int* p_height, int* p_width_create, int* p_height_create); + void texture_compute_size_type(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); + ogl_texture_info *texture_create(const render_texinfo *texsource, uint32_t flags); + int texture_shader_create(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); + ogl_texture_info *texture_find(const render_primitive *prim); + void texture_coord_update(ogl_texture_info *texture, const render_primitive *prim, int shaderIdx); + void texture_mpass_flip(ogl_texture_info *texture, int shaderIdx); + void texture_shader_update(ogl_texture_info *texture, render_container *container, int shaderIdx); + ogl_texture_info * texture_update(const render_primitive *prim, int shaderIdx); + void texture_disable(ogl_texture_info * texture); + void texture_all_disable(); + + int gl_checkFramebufferStatus() const; + int texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t fbo_name, int width, int height) const; + void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags) const; + + int gl_check_error(bool log, const char *file, int line) const + { + GLenum const glerr = glGetError(); + if (GL_NO_ERROR != glerr) + { + if (log) + osd_printf_warning("%s:%d: GL Error: %d 0x%X\n", file, line, int(glerr), unsigned(glerr)); + } + return (GL_NO_ERROR != glerr) ? glerr : 0; + } + +#define GL_CHECK_ERROR_QUIET() gl_check_error(false, __FILE__, __LINE__) +#define GL_CHECK_ERROR_NORMAL() gl_check_error(true, __FILE__, __LINE__) + + ogl_video_config const &m_ogl_config; + + int32_t m_blittimer; + int m_width; + int m_height; + osd_dim m_blit_dim; + + std::unique_ptr<osd_gl_context> m_gl_context; + std::unique_ptr<glsl_shader_info> m_shader_tool; + + int m_initialized; // is everything well initialized, i.e. all GL stuff etc. + // 3D info (GL mode only) + ogl_texture_info * m_texhash[HASH_SIZE + OVERFLOW_SIZE]; + int m_last_blendmode; // previous blendmode + int32_t m_texture_max_width; // texture maximum width + int32_t m_texture_max_height; // texture maximum height + int m_texpoweroftwo; // must textures be power-of-2 sized? + int m_usevbo; // runtime check if VBO is available + int m_usepbo; // runtime check if PBO is available + int m_usefbo; // runtime check if FBO is available + int m_useglsl; // runtime check if GLSL is available + + GLhandleARB m_glsl_program[2*GLSL_SHADER_MAX]; // GLSL programs, or 0 + int m_glsl_program_num; // number of GLSL programs + int m_glsl_program_mb2sc; // GLSL program idx, which transforms + // the mame-bitmap. screen-bitmap (size/rotation/..) + // All progs <= glsl_program_mb2sc using the mame bitmap + // as input, otherwise the screen bitmap. + // All progs >= glsl_program_mb2sc using the screen bitmap + // as output, otherwise the mame bitmap. + int m_usetexturerect; // use ARB_texture_rectangle for non-power-of-2, general use + + int m_init_context; // initialize context before next draw + + float m_last_hofs; + float m_last_vofs; + + // Static vars from draogl_window_dra + int32_t m_surf_w; + int32_t m_surf_h; + GLfloat m_texVerticex[8]; + +#if defined(USE_DISPATCH_GL) + std::unique_ptr<osd_gl_dispatch> gl_dispatch; // name is magic, can't be changed +#endif + + // OGL 1.3 +#if defined(GL_ARB_multitexture) && !defined(OSD_MAC) + PFNGLACTIVETEXTUREARBPROC m_glActiveTexture = nullptr; +#else + PFNGLACTIVETEXTUREPROC m_glActiveTexture = nullptr; +#endif + + // VBO + PFNGLGENBUFFERSPROC m_glGenBuffers = nullptr; + PFNGLDELETEBUFFERSPROC m_glDeleteBuffers = nullptr; + PFNGLBINDBUFFERPROC m_glBindBuffer = nullptr; + PFNGLBUFFERDATAPROC m_glBufferData = nullptr; + PFNGLBUFFERSUBDATAPROC m_glBufferSubData = nullptr; + + // PBO + PFNGLMAPBUFFERPROC m_glMapBuffer = nullptr; + PFNGLUNMAPBUFFERPROC m_glUnmapBuffer = nullptr; + + // FBO + PFNGLISFRAMEBUFFEREXTPROC m_glIsFramebuffer = nullptr; + PFNGLBINDFRAMEBUFFEREXTPROC m_glBindFramebuffer = nullptr; + PFNGLDELETEFRAMEBUFFERSEXTPROC m_glDeleteFramebuffers = nullptr; + PFNGLGENFRAMEBUFFERSEXTPROC m_glGenFramebuffers = nullptr; + PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC m_glCheckFramebufferStatus = nullptr; + PFNGLFRAMEBUFFERTEXTURE2DEXTPROC m_glFramebufferTexture2D = nullptr; + + static bool s_shown_video_info; +}; + //============================================================ // DEBUGGING @@ -241,103 +546,22 @@ void renderer_ogl::set_blendmode(int blendmode) } } -//============================================================ -// STATIC VARIABLES -//============================================================ - -// OGL 1.3 -#if defined(GL_ARB_multitexture) && !defined(OSD_MAC) -static PFNGLACTIVETEXTUREARBPROC pfn_glActiveTexture = nullptr; -#else -static PFNGLACTIVETEXTUREPROC pfn_glActiveTexture = nullptr; -#endif - -// VBO -static PFNGLGENBUFFERSPROC pfn_glGenBuffers = nullptr; -static PFNGLDELETEBUFFERSPROC pfn_glDeleteBuffers = nullptr; -static PFNGLBINDBUFFERPROC pfn_glBindBuffer = nullptr; -static PFNGLBUFFERDATAPROC pfn_glBufferData = nullptr; -static PFNGLBUFFERSUBDATAPROC pfn_glBufferSubData = nullptr; - -// PBO -static PFNGLMAPBUFFERPROC pfn_glMapBuffer = nullptr; -static PFNGLUNMAPBUFFERPROC pfn_glUnmapBuffer = nullptr; - -// FBO -static PFNGLISFRAMEBUFFEREXTPROC pfn_glIsFramebuffer = nullptr; -static PFNGLBINDFRAMEBUFFEREXTPROC pfn_glBindFramebuffer = nullptr; -static PFNGLDELETEFRAMEBUFFERSEXTPROC pfn_glDeleteFramebuffers = nullptr; -static PFNGLGENFRAMEBUFFERSEXTPROC pfn_glGenFramebuffers = nullptr; -static PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pfn_glCheckFramebufferStatus = nullptr; -static PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pfn_glFramebufferTexture2D = nullptr; - -static int glsl_shader_feature = GLSL_SHADER_FEAT_PLAIN; - -//============================================================ -// Textures -//============================================================ - -static void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags); +static int glsl_shader_feature = glsl_shader_info::FEAT_PLAIN; // FIXME: why is this static? //============================================================ // Static Variables //============================================================ bool renderer_ogl::s_shown_video_info = false; -bool renderer_ogl::s_dll_loaded = false; - -void renderer_ogl::init(running_machine &machine) -{ - s_dll_loaded = false; - - load_gl_lib(machine); -#if defined(OSD_WINDOWS) - osd_printf_verbose("Using Windows OpenGL driver\n"); -#else - osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n"); -#endif -} - -//============================================================ -// CONSTRUCTOR & DESTRUCTOR -//============================================================ - -renderer_ogl::~renderer_ogl() -{ - // free the memory in the window - destroy_all_textures(); - - delete m_gl_context; - m_gl_context = nullptr; -} - -void renderer_ogl::exit() -{ - for (int i = 0; i < video_config.glsl_shader_mamebm_num; i++) - { - if (nullptr != video_config.glsl_shader_mamebm[i]) - { - free(video_config.glsl_shader_mamebm[i]); - video_config.glsl_shader_mamebm[i] = nullptr; - } - } - for (int i =0; i < video_config.glsl_shader_scrn_num; i++) - { - if (nullptr != video_config.glsl_shader_scrn[i]) - { - free(video_config.glsl_shader_scrn[i]); - video_config.glsl_shader_scrn[i] = nullptr; - } - } -} //============================================================ // Load the OGL function addresses //============================================================ -static void loadgl_functions(osd_gl_context *context) +void renderer_ogl::loadgl_functions() { -#ifdef USE_DISPATCH_GL +#if defined(USE_DISPATCH_GL) + gl_dispatch = std::make_unique<osd_gl_dispatch>(); int err_count = 0; @@ -345,11 +569,11 @@ static void loadgl_functions(osd_gl_context *context) * while func will be expanded to disp_p->glBegin */ - #define OSD_GL(ret,func,params) \ - if (!( func = (ret (APIENTRY *)params) context->getProcAddress( #func ) )) \ - { err_count++; osd_printf_error("GL function %s not found!\n", #func ); } + #define OSD_GL(ret, func, params) \ + if (!m_gl_context->get_proc_address(func, #func)) \ + { err_count++; osd_printf_error("GL function %s not found!\n", #func); } - #define OSD_GL_UNUSED(ret,func,params) + #define OSD_GL_UNUSED(ret, func, params) #define GET_GLFUNC 1 #include "modules/opengl/osd_opengl.h" @@ -361,45 +585,6 @@ static void loadgl_functions(osd_gl_context *context) #endif } -//============================================================ -// Load GL library -//============================================================ - -#ifdef USE_DISPATCH_GL -osd_gl_dispatch *gl_dispatch = nullptr; -#endif - -void renderer_ogl::load_gl_lib(running_machine &machine) -{ - if (!s_dll_loaded) - { -#ifndef OSD_WINDOWS -#ifdef USE_DISPATCH_GL - /* - * directfb and and x11 use this env var - * SDL_VIDEO_GL_DRIVER - */ - const char *stemp; - - stemp = downcast<sdl_options &>(machine.options()).gl_lib(); - if (stemp != nullptr && strcmp(stemp, OSDOPTVAL_AUTO) == 0) - stemp = nullptr; - - if (SDL_GL_LoadLibrary(stemp) != 0) // Load library (default for e==nullptr - { - fatalerror("Unable to load opengl library: %s\n", stemp ? stemp : "<default>"); - } - osd_printf_verbose("Loaded opengl shared library: %s\n", stemp ? stemp : "<default>"); - /* FIXME: must be freed as well */ -#endif -#endif -#ifdef USE_DISPATCH_GL - gl_dispatch = new osd_gl_dispatch; -#endif - s_dll_loaded = true; - } -} - void renderer_ogl::initialize_gl() { int has_and_allow_texturerect = 0; @@ -425,7 +610,7 @@ void renderer_ogl::initialize_gl() m_usefbo = 0; m_useglsl = 0; - if (video_config.allowtexturerect && (strstr(extstr, "GL_ARB_texture_rectangle") || strstr(extstr, "GL_EXT_texture_rectangle"))) + if (m_ogl_config.allowtexturerect && (strstr(extstr, "GL_ARB_texture_rectangle") || strstr(extstr, "GL_EXT_texture_rectangle"))) { has_and_allow_texturerect = 1; if (!s_shown_video_info) @@ -435,7 +620,7 @@ void renderer_ogl::initialize_gl() } // does this card support non-power-of-two sized textures? (they're faster, so use them if possible) - if ( !video_config.forcepow2texture && strstr(extstr, "GL_ARB_texture_non_power_of_two")) + if ( !m_ogl_config.forcepow2texture && strstr(extstr, "GL_ARB_texture_non_power_of_two")) { if (!s_shown_video_info) { @@ -465,7 +650,7 @@ void renderer_ogl::initialize_gl() if (strstr(extstr, "GL_ARB_vertex_buffer_object")) { - m_usevbo = video_config.vbo; + m_usevbo = m_ogl_config.vbo; if (!s_shown_video_info) { if(m_usevbo) @@ -479,7 +664,7 @@ void renderer_ogl::initialize_gl() { if( m_usevbo ) { - m_usepbo = video_config.pbo; + m_usepbo = m_ogl_config.pbo; if (!s_shown_video_info) { if(m_usepbo) @@ -522,7 +707,7 @@ void renderer_ogl::initialize_gl() strstr(extstr, "GL_ARB_fragment_shader") ) { - m_useglsl = video_config.glsl; + m_useglsl = m_ogl_config.glsl; if (!s_shown_video_info) { if(m_useglsl) @@ -562,24 +747,22 @@ void renderer_ogl::initialize_gl() int renderer_ogl::create() { - auto win = assert_window(); - // create renderer #if defined(OSD_WINDOWS) - m_gl_context = new win_gl_context(std::static_pointer_cast<win_window_info>(win)->platform_window()); + m_gl_context.reset(new win_gl_context(dynamic_cast<win_window_info &>(window()).platform_window())); #elif defined(OSD_MAC) // TODO -// m_gl_context = new mac_gl_context(std::static_pointer_cast<mac_window_info>(win)->platform_window()); +// m_gl_context.reset(new mac_gl_context(dynamic_cast<mac_window_info &>(window()).platform_window())); #else - m_gl_context = new sdl_gl_context(std::static_pointer_cast<sdl_window_info>(win)->platform_window()); + m_gl_context.reset(new sdl_gl_context(dynamic_cast<sdl_window_info &>(window()).platform_window())); #endif - if (m_gl_context->LastErrorMsg() != nullptr) + if (!*m_gl_context) { - osd_printf_error("%s\n", m_gl_context->LastErrorMsg()); + char const *const msg = m_gl_context->last_error_message(); + osd_printf_error("Creating OpenGL context failed: %s\n", msg ? msg : "unknown error"); return 1; } - m_gl_context->SetSwapInterval(video_config.waitvsync ? 1 : 0); - + m_gl_context->set_swap_interval(video_config.waitvsync ? 1 : 0); m_blittimer = 0; m_surf_w = 0; @@ -595,10 +778,9 @@ int renderer_ogl::create() /* load any GL function addresses * this must be done here because we need a context */ - loadgl_functions(m_gl_context); + loadgl_functions(); initialize_gl(); - m_init_context = 0; osd_printf_verbose("Leave renderer_ogl::create\n"); @@ -628,27 +810,15 @@ int renderer_ogl::xy_to_render_target(int x, int y, int *xt, int *yt) void renderer_ogl::destroy_all_textures() { - ogl_texture_info *texture = nullptr; - bool lock=false; - int i; - if ( !m_initialized ) + if (!m_initialized) return; - auto win = try_getwindow(); - - // During destroy this can get called - // and the window is no longer available - if (win == nullptr) - return; + m_gl_context->make_current(); - m_gl_context->MakeCurrent(); - - if(win->m_primlist) - { - lock=true; - win->m_primlist->acquire_lock(); - } + bool const lock = bool(window().m_primlist); + if (lock) + window().m_primlist->acquire_lock(); glFinish(); @@ -656,41 +826,39 @@ void renderer_ogl::destroy_all_textures() glFinish(); glDisableClientState(GL_VERTEX_ARRAY); - i=0; - while (i<HASH_SIZE+OVERFLOW_SIZE) + for (int i = 0; i < (HASH_SIZE + OVERFLOW_SIZE); ++i) { - texture = m_texhash[i]; - m_texhash[i] = nullptr; - if (texture != nullptr) + ogl_texture_info *const texture = std::exchange(m_texhash[i], nullptr); + if (texture) { - if(m_usevbo) + if (m_usevbo) { - pfn_glDeleteBuffers( 1, &(texture->texCoordBufferName) ); + m_glDeleteBuffers( 1, &(texture->texCoordBufferName) ); texture->texCoordBufferName=0; } - if(m_usepbo && texture->pbo) + if (m_usepbo && texture->pbo) { - pfn_glDeleteBuffers( 1, (GLuint *)&(texture->pbo) ); + m_glDeleteBuffers( 1, (GLuint *)&(texture->pbo) ); texture->pbo=0; } - if( m_glsl_program_num > 1 ) + if (m_glsl_program_num > 1) { assert(m_usefbo); - pfn_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); + m_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); glDeleteTextures(2, (GLuint *)&texture->mpass_texture_mamebm[0]); } - if ( m_glsl_program_mb2sc < m_glsl_program_num - 1 ) + if (m_glsl_program_mb2sc < m_glsl_program_num - 1) { assert(m_usefbo); - pfn_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); + m_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); glDeleteTextures(2, (GLuint *)&texture->mpass_texture_scrn[0]); } glDeleteTextures(1, (GLuint *)&texture->texture); - if ( texture->data_own ) + if (texture->data_own) { free(texture->data); texture->data=nullptr; @@ -698,18 +866,14 @@ void renderer_ogl::destroy_all_textures() } delete texture; } - i++; - } - if ( m_useglsl ) - { - glsl_shader_free(m_glsl); - m_glsl = nullptr; } + m_shader_tool.reset(); + m_initialized = 0; if (lock) - win->m_primlist->release_lock(); + window().m_primlist->release_lock(); } //============================================================ // loadGLExtensions @@ -745,178 +909,138 @@ void renderer_ogl::loadGLExtensions() // Get Pointers To The GL Functions // VBO: - if( m_usevbo ) + if (m_usevbo) { - pfn_glGenBuffers = (PFNGLGENBUFFERSPROC) m_gl_context->getProcAddress("glGenBuffers"); - pfn_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) m_gl_context->getProcAddress("glDeleteBuffers"); - pfn_glBindBuffer = (PFNGLBINDBUFFERPROC) m_gl_context->getProcAddress("glBindBuffer"); - pfn_glBufferData = (PFNGLBUFFERDATAPROC) m_gl_context->getProcAddress("glBufferData"); - pfn_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) m_gl_context->getProcAddress("glBufferSubData"); + m_gl_context->get_proc_address(m_glGenBuffers, "glGenBuffers"); + m_gl_context->get_proc_address(m_glDeleteBuffers, "glDeleteBuffers"); + m_gl_context->get_proc_address(m_glBindBuffer, "glBindBuffer"); + m_gl_context->get_proc_address(m_glBufferData, "glBufferData"); + m_gl_context->get_proc_address(m_glBufferSubData, "glBufferSubData"); } // PBO: - if ( m_usepbo ) + if (m_usepbo) { - pfn_glMapBuffer = (PFNGLMAPBUFFERPROC) m_gl_context->getProcAddress("glMapBuffer"); - pfn_glUnmapBuffer= (PFNGLUNMAPBUFFERPROC) m_gl_context->getProcAddress("glUnmapBuffer"); + m_gl_context->get_proc_address(m_glMapBuffer, "glMapBuffer"); + m_gl_context->get_proc_address(m_glUnmapBuffer, "glUnmapBuffer"); } // FBO: - if ( m_usefbo ) + if (m_usefbo) { - pfn_glIsFramebuffer = (PFNGLISFRAMEBUFFEREXTPROC) m_gl_context->getProcAddress("glIsFramebufferEXT"); - pfn_glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) m_gl_context->getProcAddress("glBindFramebufferEXT"); - pfn_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC) m_gl_context->getProcAddress("glDeleteFramebuffersEXT"); - pfn_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) m_gl_context->getProcAddress("glGenFramebuffersEXT"); - pfn_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) m_gl_context->getProcAddress("glCheckFramebufferStatusEXT"); - pfn_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) m_gl_context->getProcAddress("glFramebufferTexture2DEXT"); + m_gl_context->get_proc_address(m_glIsFramebuffer, "glIsFramebufferEXT"); + m_gl_context->get_proc_address(m_glBindFramebuffer, "glBindFramebufferEXT"); + m_gl_context->get_proc_address(m_glDeleteFramebuffers, "glDeleteFramebuffersEXT"); + m_gl_context->get_proc_address(m_glGenFramebuffers, "glGenFramebuffersEXT"); + m_gl_context->get_proc_address(m_glCheckFramebufferStatus, "glCheckFramebufferStatusEXT"); + m_gl_context->get_proc_address(m_glFramebufferTexture2D, "glFramebufferTexture2DEXT"); } - if ( m_usevbo && - ( !pfn_glGenBuffers || !pfn_glDeleteBuffers || - !pfn_glBindBuffer || !pfn_glBufferData || !pfn_glBufferSubData - ) ) + if (m_usevbo && (!m_glGenBuffers || !m_glDeleteBuffers || !m_glBindBuffer || !m_glBufferData || !m_glBufferSubData)) { - m_usepbo=false; + m_usepbo = false; if (_once) { - osd_printf_warning("OpenGL: VBO not supported, missing: "); - if (!pfn_glGenBuffers) - { - osd_printf_warning("glGenBuffers, "); - } - if (!pfn_glDeleteBuffers) - { - osd_printf_warning("glDeleteBuffers"); - } - if (!pfn_glBindBuffer) - { - osd_printf_warning("glBindBuffer, "); - } - if (!pfn_glBufferData) - { - osd_printf_warning("glBufferData, "); - } - if (!pfn_glBufferSubData) - { - osd_printf_warning("glBufferSubData, "); - } + osd_printf_warning("OpenGL: VBO not supported, missing:"); + if (!m_glGenBuffers) + osd_printf_warning(" glGenBuffers"); + if (!m_glDeleteBuffers) + osd_printf_warning(" glDeleteBuffers"); + if (!m_glBindBuffer) + osd_printf_warning(" glBindBuffer"); + if (!m_glBufferData) + osd_printf_warning(" glBufferData"); + if (!m_glBufferSubData) + osd_printf_warning(" glBufferSubData"); osd_printf_warning("\n"); } - if ( m_usevbo ) + if (m_usevbo) { if (_once) - { osd_printf_warning("OpenGL: PBO not supported, no VBO support.\n"); - } - m_usepbo=false; + m_usepbo = false; } } - if ( m_usepbo && ( !pfn_glMapBuffer || !pfn_glUnmapBuffer ) ) + if (m_usepbo && (!m_glMapBuffer || !m_glUnmapBuffer)) { - m_usepbo=false; + m_usepbo = false; if (_once) { - osd_printf_warning("OpenGL: PBO not supported, missing: "); - if (!pfn_glMapBuffer) - { - osd_printf_warning("glMapBuffer, "); - } - if (!pfn_glUnmapBuffer) - { - osd_printf_warning("glUnmapBuffer, "); - } + osd_printf_warning("OpenGL: PBO not supported, missing:"); + if (!m_glMapBuffer) + osd_printf_warning(" glMapBuffer"); + if (!m_glUnmapBuffer) + osd_printf_warning(" glUnmapBuffer"); osd_printf_warning("\n"); } } if ( m_usefbo && - ( !pfn_glIsFramebuffer || !pfn_glBindFramebuffer || !pfn_glDeleteFramebuffers || - !pfn_glGenFramebuffers || !pfn_glCheckFramebufferStatus || !pfn_glFramebufferTexture2D + ( !m_glIsFramebuffer || !m_glBindFramebuffer || !m_glDeleteFramebuffers || + !m_glGenFramebuffers || !m_glCheckFramebufferStatus || !m_glFramebufferTexture2D )) { - m_usefbo=false; + m_usefbo = false; if (_once) { - osd_printf_warning("OpenGL: FBO not supported, missing: "); - if (!pfn_glIsFramebuffer) - { - osd_printf_warning("pfn_glIsFramebuffer, "); - } - if (!pfn_glBindFramebuffer) - { - osd_printf_warning("pfn_glBindFramebuffer, "); - } - if (!pfn_glDeleteFramebuffers) - { - osd_printf_warning("pfn_glDeleteFramebuffers, "); - } - if (!pfn_glGenFramebuffers) - { - osd_printf_warning("pfn_glGenFramebuffers, "); - } - if (!pfn_glCheckFramebufferStatus) - { - osd_printf_warning("pfn_glCheckFramebufferStatus, "); - } - if (!pfn_glFramebufferTexture2D) - { - osd_printf_warning("pfn_glFramebufferTexture2D, "); - } + osd_printf_warning("OpenGL: FBO not supported, missing:"); + if (!m_glIsFramebuffer) + osd_printf_warning(" m_glIsFramebuffer"); + if (!m_glBindFramebuffer) + osd_printf_warning(" m_glBindFramebuffer"); + if (!m_glDeleteFramebuffers) + osd_printf_warning(" m_glDeleteFramebuffers"); + if (!m_glGenFramebuffers) + osd_printf_warning(" m_glGenFramebuffers"); + if (!m_glCheckFramebufferStatus) + osd_printf_warning(" m_glCheckFramebufferStatus"); + if (!m_glFramebufferTexture2D) + osd_printf_warning(" m_glFramebufferTexture2D"); osd_printf_warning("\n"); } } if (_once) { - if ( m_usevbo ) - { + if (m_usevbo) osd_printf_verbose("OpenGL: VBO supported\n"); - } else - { osd_printf_warning("OpenGL: VBO not supported\n"); - } - if ( m_usepbo ) - { + if (m_usepbo) osd_printf_verbose("OpenGL: PBO supported\n"); - } else - { osd_printf_warning("OpenGL: PBO not supported\n"); - } - if ( m_usefbo ) - { + if (m_usefbo) osd_printf_verbose("OpenGL: FBO supported\n"); - } else - { osd_printf_warning("OpenGL: FBO not supported\n"); - } } - if ( m_useglsl ) + if (m_useglsl) { #if defined(GL_ARB_multitexture) && !defined(OSD_MAC) - pfn_glActiveTexture = (PFNGLACTIVETEXTUREARBPROC) m_gl_context->getProcAddress("glActiveTextureARB"); + m_gl_context->get_proc_address(m_glActiveTexture, "glActiveTextureARB"); #else - pfn_glActiveTexture = (PFNGLACTIVETEXTUREPROC) m_gl_context->getProcAddress("glActiveTexture"); + m_gl_context->get_proc_address(m_glActiveTexture, "glActiveTexture"); #endif - if (!pfn_glActiveTexture) + if (!m_glActiveTexture) { if (_once) - { osd_printf_warning("OpenGL: GLSL disabled, glActiveTexture(ARB) not supported\n"); - } m_useglsl = 0; } } - if ( m_useglsl ) + if (m_useglsl) { - m_glsl = glsl_shader_init(m_gl_context); - m_useglsl = (m_glsl != nullptr ? 1 : 0); + m_shader_tool = glsl_shader_info::init( + *m_gl_context +#if defined(USE_DISPATCH_GL) + , gl_dispatch.get() +#endif + ); + m_useglsl = (m_shader_tool ? 1 : 0); if ( ! m_useglsl ) { @@ -927,9 +1051,9 @@ void renderer_ogl::loadGLExtensions() } } - if ( m_useglsl ) + if (m_useglsl) { - if (assert_window()->prescale() != 1 ) + if (window().prescale() != 1 ) { m_useglsl = 0; if (_once) @@ -939,15 +1063,14 @@ void renderer_ogl::loadGLExtensions() } } - if ( m_useglsl ) + if (m_useglsl) { - int i; video_config.filter = false; - glsl_shader_feature = GLSL_SHADER_FEAT_PLAIN; + glsl_shader_feature = glsl_shader_info::FEAT_PLAIN; m_glsl_program_num = 0; m_glsl_program_mb2sc = 0; - for(i=0; i<video_config.glsl_shader_mamebm_num; i++) + for (int i=0; i<m_ogl_config.glsl_shader_mamebm_num; i++) { if ( !m_usefbo && m_glsl_program_num==1 ) { @@ -958,60 +1081,65 @@ void renderer_ogl::loadGLExtensions() break; } - if ( glsl_shader_add_mamebm(m_glsl, video_config.glsl_shader_mamebm[i], m_glsl_program_num) ) + if ( m_shader_tool->add_mamebm(m_ogl_config.glsl_shader_mamebm[i].c_str(), m_glsl_program_num) ) { osd_printf_error("OpenGL: GLSL loading mame bitmap shader %d failed (%s)\n", - i, video_config.glsl_shader_mamebm[i]); - } else { - glsl_shader_feature = GLSL_SHADER_FEAT_CUSTOM; + i, m_ogl_config.glsl_shader_mamebm[i]); + } + else + { + glsl_shader_feature = glsl_shader_info::FEAT_CUSTOM; if (_once) { osd_printf_verbose("OpenGL: GLSL using mame bitmap shader filter %d: '%s'\n", - m_glsl_program_num, video_config.glsl_shader_mamebm[i]); + m_glsl_program_num, m_ogl_config.glsl_shader_mamebm[i]); } m_glsl_program_mb2sc = m_glsl_program_num; // the last mame_bitmap (mb) shader does it. m_glsl_program_num++; } } - if ( video_config.glsl_shader_scrn_num > 0 && m_glsl_program_num==0 ) + if ( m_ogl_config.glsl_shader_scrn_num > 0 && m_glsl_program_num==0 ) { osd_printf_verbose("OpenGL: GLSL cannot use screen bitmap shader without bitmap shader\n"); } - for(i=0; m_usefbo && m_glsl_program_num>0 && i<video_config.glsl_shader_scrn_num; i++) + for(int i=0; m_usefbo && m_glsl_program_num>0 && i<m_ogl_config.glsl_shader_scrn_num; i++) { - if ( glsl_shader_add_scrn(m_glsl, video_config.glsl_shader_scrn[i], - m_glsl_program_num-1-m_glsl_program_mb2sc) ) + if ( m_shader_tool->add_scrn(m_ogl_config.glsl_shader_scrn[i].c_str(), m_glsl_program_num-1-m_glsl_program_mb2sc) ) { osd_printf_error("OpenGL: GLSL loading screen bitmap shader %d failed (%s)\n", - i, video_config.glsl_shader_scrn[i]); - } else { + i, m_ogl_config.glsl_shader_scrn[i]); + } + else + { if (_once) { osd_printf_verbose("OpenGL: GLSL using screen bitmap shader filter %d: '%s'\n", - m_glsl_program_num, video_config.glsl_shader_scrn[i]); + m_glsl_program_num, m_ogl_config.glsl_shader_scrn[i]); } m_glsl_program_num++; } } if ( 0==m_glsl_program_num && - 0 <= video_config.glsl_filter && video_config.glsl_filter < GLSL_SHADER_FEAT_INT_NUMBER ) + 0 <= m_ogl_config.glsl_filter && m_ogl_config.glsl_filter < glsl_shader_info::FEAT_INT_NUMBER ) { m_glsl_program_mb2sc = m_glsl_program_num; // the last mame_bitmap (mb) shader does it. m_glsl_program_num++; - glsl_shader_feature = video_config.glsl_filter; + glsl_shader_feature = m_ogl_config.glsl_filter; if (_once) { osd_printf_verbose("OpenGL: GLSL using shader filter '%s', idx: %d, num %d (vid filter: %d)\n", - glsl_shader_get_filter_name_mamebm(glsl_shader_feature), + m_shader_tool->get_filter_name_mamebm(glsl_shader_feature), glsl_shader_feature, m_glsl_program_num, video_config.filter); } } - } else { + } + else + { if (_once) { osd_printf_verbose("OpenGL: using vid filter: %d\n", video_config.filter); @@ -1031,16 +1159,7 @@ int renderer_ogl::draw(const int update) float vofs, hofs; int pendingPrimitive=GL_NO_PRIMITIVE, curPrimitive=GL_NO_PRIMITIVE; -#ifdef TOBEMIGRATED - if (video_config.novideo) - { - return 0; - } -#endif - - auto win = assert_window(); - - osd_dim wdim = win->get_size(); + osd_dim wdim = window().get_size(); if (has_flags(FI_CHANGED) || (wdim.width() != m_width) || (wdim.height() != m_height)) { @@ -1052,7 +1171,7 @@ int renderer_ogl::draw(const int update) clear_flags(FI_CHANGED); } - m_gl_context->MakeCurrent(); + m_gl_context->make_current(); if (m_init_context) { @@ -1192,16 +1311,15 @@ int renderer_ogl::draw(const int update) hofs = (cw - m_blit_dim.width()) / 2.0f; } } -#else #endif m_last_hofs = hofs; m_last_vofs = vofs; - win->m_primlist->acquire_lock(); + window().m_primlist->acquire_lock(); // now draw - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { int i; @@ -1236,7 +1354,10 @@ int renderer_ogl::draw(const int update) if(pendingPrimitive!=curPrimitive) { - glLineWidth(prim.width); + if (curPrimitive==GL_POINTS) + glPointSize(prim.width); + else + glLineWidth(prim.width); glBegin(curPrimitive); pendingPrimitive=curPrimitive; } @@ -1407,10 +1528,10 @@ int renderer_ogl::draw(const int update) pendingPrimitive=GL_NO_PRIMITIVE; } - win->m_primlist->release_lock(); + window().m_primlist->release_lock(); m_init_context = 0; - m_gl_context->SwapBuffer(); + m_gl_context->swap_buffer(); return 0; } @@ -1543,9 +1664,8 @@ void renderer_ogl::texture_compute_size_subroutine(ogl_texture_info *texture, ui while (texture->yprescale > 1 && height_create * texture->yprescale > m_texture_max_height) texture->yprescale--; - auto win = assert_window(); - if (PRIMFLAG_GET_SCREENTEX(flags) && (texture->xprescale != win->prescale() || texture->yprescale != win->prescale())) - osd_printf_warning("SDL: adjusting prescale from %dx%d to %dx%d\n", win->prescale(), win->prescale(), texture->xprescale, texture->yprescale); + if (PRIMFLAG_GET_SCREENTEX(flags) && (texture->xprescale != window().prescale() || texture->yprescale != window().prescale())) + osd_printf_warning("SDL: adjusting prescale from %dx%d to %dx%d\n", window().prescale(), window().prescale(), texture->xprescale, texture->yprescale); width *= texture->xprescale; height *= texture->yprescale; @@ -1640,10 +1760,9 @@ void renderer_ogl::texture_compute_size_type(const render_texinfo *texsource, og // texture_create //============================================================ -static int gl_checkFramebufferStatus() +int renderer_ogl::gl_checkFramebufferStatus() const { - GLenum status; - status=(GLenum)pfn_glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + GLenum const status = (GLenum)m_glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); switch(status) { case GL_FRAMEBUFFER_COMPLETE_EXT: return 0; @@ -1683,14 +1802,14 @@ static int gl_checkFramebufferStatus() return -1; } -static int texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t fbo_name, int width, int height) +int renderer_ogl::texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t fbo_name, int width, int height) const { - pfn_glActiveTexture(text_unit); - pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_name); + m_glActiveTexture(text_unit); + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_name); glBindTexture(GL_TEXTURE_2D, text_name); { GLint _width, _height; - if ( gl_texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, + if ( m_shader_tool->texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &_width, &_height, 1) ) { osd_printf_error("cannot create fbo texture, req: %dx%d, avail: %dx%d - bail out\n", @@ -1708,7 +1827,7 @@ static int texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t f glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - pfn_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + m_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, text_name, 0); if ( gl_checkFramebufferStatus() ) @@ -1745,51 +1864,49 @@ int renderer_ogl::texture_shader_create(const render_texinfo *texsource, ogl_tex for(i=0; i<m_glsl_program_num; i++) { if ( i<=m_glsl_program_mb2sc ) - { - m_glsl_program[i] = glsl_shader_get_program_mamebm(glsl_shader_feature, i); - } else { - m_glsl_program[i] = glsl_shader_get_program_scrn(i-1-m_glsl_program_mb2sc); - } - pfn_glUseProgramObjectARB(m_glsl_program[i]); + m_glsl_program[i] = m_shader_tool->get_program_mamebm(glsl_shader_feature, i); + else + m_glsl_program[i] = m_shader_tool->get_program_scrn(i-1-m_glsl_program_mb2sc); + m_shader_tool->pfn_glUseProgramObjectARB(m_glsl_program[i]); if ( i<=m_glsl_program_mb2sc ) { // GL_TEXTURE0 GLSL Uniforms - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture"); - pfn_glUniform1iARB(uniform_location, 0); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture"); + m_shader_tool->pfn_glUniform1iARB(uniform_location, 0); GL_CHECK_ERROR_NORMAL(); } { GLfloat color_texture_sz[2] = { (GLfloat)texture->rawwidth, (GLfloat)texture->rawheight }; - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_sz"); - pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_sz[0])); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_sz"); + m_shader_tool->pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_sz[0])); GL_CHECK_ERROR_NORMAL(); } GLfloat color_texture_pow2_sz[2] = { (GLfloat)texture->rawwidth_create, (GLfloat)texture->rawheight_create }; - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_pow2_sz"); - pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_pow2_sz[0])); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_pow2_sz"); + m_shader_tool->pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_pow2_sz[0])); GL_CHECK_ERROR_NORMAL(); GLfloat screen_texture_sz[2] = { (GLfloat) m_blit_dim.width(), (GLfloat) m_blit_dim.height() }; - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_sz"); - pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_sz[0])); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_sz"); + m_shader_tool->pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_sz[0])); GL_CHECK_ERROR_NORMAL(); GLfloat screen_texture_pow2_sz[2] = { (GLfloat)surf_w_pow2, (GLfloat)surf_h_pow2 }; - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_pow2_sz"); - pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_pow2_sz[0])); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_pow2_sz"); + m_shader_tool->pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_pow2_sz[0])); GL_CHECK_ERROR_NORMAL(); } - pfn_glUseProgramObjectARB(m_glsl_program[0]); // start with 1st shader + m_shader_tool->pfn_glUseProgramObjectARB(m_glsl_program[0]); // start with 1st shader if( m_glsl_program_num > 1 ) { // multipass mode // GL_TEXTURE2/GL_TEXTURE3 - pfn_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); + m_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); glGenTextures(2, (GLuint *)&texture->mpass_texture_mamebm[0]); for (i=0; i<2; i++) @@ -1803,7 +1920,7 @@ int renderer_ogl::texture_shader_create(const render_texinfo *texsource, ogl_tex } } - pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); osd_printf_verbose("GL texture: mpass mame-bmp 2x %dx%d (pow2 %dx%d)\n", texture->rawwidth, texture->rawheight, texture->rawwidth_create, texture->rawheight_create); @@ -1813,7 +1930,7 @@ int renderer_ogl::texture_shader_create(const render_texinfo *texsource, ogl_tex { // multipass mode // GL_TEXTURE2/GL_TEXTURE3 - pfn_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); + m_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); glGenTextures(2, (GLuint *)&texture->mpass_texture_scrn[0]); for (i=0; i<2; i++) @@ -1834,14 +1951,14 @@ int renderer_ogl::texture_shader_create(const render_texinfo *texsource, ogl_tex // GL_TEXTURE0 // get a name for this texture glGenTextures(1, (GLuint *)&texture->texture); - pfn_glActiveTexture(GL_TEXTURE0); + m_glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture->texture); glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth_create); uint32_t * dummy = nullptr; GLint _width, _height; - if ( gl_texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, + if ( m_shader_tool->texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, texture->rawwidth_create, texture->rawheight_create, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, @@ -1863,7 +1980,7 @@ int renderer_ogl::texture_shader_create(const render_texinfo *texsource, ogl_tex if ((PRIMFLAG_GET_SCREENTEX(flags)) && video_config.filter) { - assert( glsl_shader_feature == GLSL_SHADER_FEAT_PLAIN ); + assert( glsl_shader_feature == glsl_shader_info::FEAT_PLAIN ); // screen textures get the user's choice of filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -1898,7 +2015,11 @@ ogl_texture_info *renderer_ogl::texture_create(const render_texinfo *texsource, ogl_texture_info *texture; // allocate a new texture - texture = new ogl_texture_info; + texture = new ogl_texture_info( +#if defined(USE_DISPATCH_GL) + gl_dispatch.get() +#endif + ); // fill in the core data texture->hash = texture_compute_hash(texsource, flags); @@ -1907,9 +2028,8 @@ ogl_texture_info *renderer_ogl::texture_create(const render_texinfo *texsource, texture->texinfo.seqid = -1; // force set data if (PRIMFLAG_GET_SCREENTEX(flags)) { - auto win = assert_window(); - texture->xprescale = win->prescale(); - texture->yprescale = win->prescale(); + texture->xprescale = window().prescale(); + texture->yprescale = window().prescale(); } else { @@ -1963,7 +2083,7 @@ ogl_texture_info *renderer_ogl::texture_create(const render_texinfo *texsource, if ( texture->type != TEXTURE_TYPE_SHADER && m_useglsl) { - pfn_glUseProgramObjectARB(0); // back to fixed function pipeline + m_shader_tool->pfn_glUseProgramObjectARB(0); // back to fixed function pipeline } if ( texture->type==TEXTURE_TYPE_SHADER ) @@ -2028,12 +2148,12 @@ ogl_texture_info *renderer_ogl::texture_create(const render_texinfo *texsource, assert(m_usepbo); // create the PBO - pfn_glGenBuffers(1, (GLuint *)&texture->pbo); + m_glGenBuffers(1, (GLuint *)&texture->pbo); - pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); + m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); // set up the PBO dimension, .. - pfn_glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, + m_glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, texture->rawwidth * texture->rawheight * sizeof(uint32_t), nullptr, GL_STREAM_DRAW); } @@ -2063,10 +2183,10 @@ ogl_texture_info *renderer_ogl::texture_create(const render_texinfo *texsource, if (m_usevbo) { // Generate And Bind The Texture Coordinate Buffer - pfn_glGenBuffers( 1, &(texture->texCoordBufferName) ); - pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); + m_glGenBuffers( 1, &(texture->texCoordBufferName) ); + m_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); // Load The Data - pfn_glBufferData( GL_ARRAY_BUFFER_ARB, 4*2*sizeof(GLfloat), texture->texCoord, GL_STREAM_DRAW ); + m_glBufferData( GL_ARRAY_BUFFER_ARB, 4*2*sizeof(GLfloat), texture->texCoord, GL_STREAM_DRAW ); glTexCoordPointer( 2, GL_FLOAT, 0, (char *) nullptr ); // we are using ARB VBO buffers } else @@ -2327,14 +2447,14 @@ static inline void copyline_yuy16_to_argb(uint32_t *dst, const uint16_t *src, in // texture_set_data //============================================================ -static void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags) +void renderer_ogl::texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags) const { if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert(texture->pbo); assert(!texture->nocopy); - texture->data = (uint32_t *) pfn_glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); + texture->data = (uint32_t *) m_glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); } // note that nocopy and borderpix are mutually exclusive, IOW @@ -2401,7 +2521,7 @@ static void texture_set_data(ogl_texture_info *texture, const render_texinfo *te if ( texture->type == TEXTURE_TYPE_SHADER ) { - pfn_glActiveTexture(GL_TEXTURE0); + m_glActiveTexture(GL_TEXTURE0); glBindTexture(texture->texTarget, texture->texture); if (texture->nocopy) @@ -2420,7 +2540,7 @@ static void texture_set_data(ogl_texture_info *texture, const render_texinfo *te glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth); // unmap the buffer from the CPU space so it can DMA - pfn_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); + m_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); // kick off the DMA glTexSubImage2D(texture->texTarget, 0, 0, 0, texture->rawwidth, texture->rawheight, @@ -2570,12 +2690,12 @@ void renderer_ogl::texture_mpass_flip(ogl_texture_info *texture, int shaderIdx) if ( shaderIdx>0 ) { int uniform_location; - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "mpass_texture"); - pfn_glUniform1iARB(uniform_location, texture->mpass_textureunit[mpass_src_idx]-GL_TEXTURE0); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "mpass_texture"); + m_shader_tool->pfn_glUniform1iARB(uniform_location, texture->mpass_textureunit[mpass_src_idx]-GL_TEXTURE0); GL_CHECK_ERROR_NORMAL(); } - pfn_glActiveTexture(texture->mpass_textureunit[mpass_src_idx]); + m_glActiveTexture(texture->mpass_textureunit[mpass_src_idx]); if ( shaderIdx<=m_glsl_program_mb2sc ) { glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[mpass_src_idx]); @@ -2584,22 +2704,22 @@ void renderer_ogl::texture_mpass_flip(ogl_texture_info *texture, int shaderIdx) { glBindTexture(texture->texTarget, texture->mpass_texture_scrn[mpass_src_idx]); } - pfn_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); + m_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); glBindTexture(texture->texTarget, 0); - pfn_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); + m_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); if ( shaderIdx<m_glsl_program_num-1 ) { if ( shaderIdx>=m_glsl_program_mb2sc ) { glBindTexture(texture->texTarget, texture->mpass_texture_scrn[texture->mpass_dest_idx]); - pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_scrn[texture->mpass_dest_idx]); + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_scrn[texture->mpass_dest_idx]); } else { glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[texture->mpass_dest_idx]); - pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_mamebm[texture->mpass_dest_idx]); + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_mamebm[texture->mpass_dest_idx]); } if ( shaderIdx==0 ) @@ -2620,7 +2740,7 @@ void renderer_ogl::texture_mpass_flip(ogl_texture_info *texture, int shaderIdx) else { glBindTexture(texture->texTarget, 0); - pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); if ( m_glsl_program_mb2sc == m_glsl_program_num-1 ) { @@ -2628,7 +2748,7 @@ void renderer_ogl::texture_mpass_flip(ogl_texture_info *texture, int shaderIdx) GL_CHECK_ERROR_NORMAL(); } - pfn_glActiveTexture(GL_TEXTURE0); + m_glActiveTexture(GL_TEXTURE0); glBindTexture(texture->texTarget, 0); } } @@ -2649,8 +2769,8 @@ void renderer_ogl::texture_shader_update(ogl_texture_info *texture, render_conta vid_attributes[1] = settings.m_contrast; vid_attributes[2] = settings.m_brightness; vid_attributes[3] = 0.0f; - uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "vid_attributes"); - pfn_glUniform4fvARB(uniform_location, 1, &(vid_attributes[shaderIdx])); + uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "vid_attributes"); + m_shader_tool->pfn_glUniform4fvARB(uniform_location, 1, &(vid_attributes[shaderIdx])); if ( GL_CHECK_ERROR_QUIET() ) { osd_printf_verbose("GLSL: could not set 'vid_attributes' for shader prog idx %d\n", shaderIdx); } @@ -2671,12 +2791,12 @@ ogl_texture_info * renderer_ogl::texture_update(const render_primitive *prim, in { if ( texture->type == TEXTURE_TYPE_SHADER ) { - pfn_glUseProgramObjectARB(m_glsl_program[shaderIdx]); // back to our shader + m_shader_tool->pfn_glUseProgramObjectARB(m_glsl_program[shaderIdx]); // back to our shader } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert ( m_usepbo ) ; - pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); + m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); glEnable(texture->texTarget); } else @@ -2716,9 +2836,9 @@ ogl_texture_info * renderer_ogl::texture_update(const render_primitive *prim, in glEnableClientState(GL_TEXTURE_COORD_ARRAY); if(m_usevbo) { - pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); + m_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); // Load The Data - pfn_glBufferSubData( GL_ARRAY_BUFFER_ARB, 0, 4*2*sizeof(GLfloat), texture->texCoord ); + m_glBufferSubData( GL_ARRAY_BUFFER_ARB, 0, 4*2*sizeof(GLfloat), texture->texCoord ); glTexCoordPointer( 2, GL_FLOAT, 0, (char *) nullptr ); // we are using ARB VBO buffers } else @@ -2735,34 +2855,41 @@ void renderer_ogl::texture_disable(ogl_texture_info * texture) if ( texture->type == TEXTURE_TYPE_SHADER ) { assert ( m_useglsl ); - pfn_glUseProgramObjectARB(0); // back to fixed function pipeline - } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) + m_shader_tool->pfn_glUseProgramObjectARB(0); // back to fixed function pipeline + } + else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { - pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); + m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); glDisable(texture->texTarget); - } else { + } + else + { glDisable(texture->texTarget); } } void renderer_ogl::texture_all_disable() { - if ( m_useglsl ) + if (m_useglsl) { - pfn_glUseProgramObjectARB(0); // back to fixed function pipeline + m_shader_tool->pfn_glUseProgramObjectARB(0); // back to fixed function pipeline - pfn_glActiveTexture(GL_TEXTURE3); + m_glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, 0); - if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); - pfn_glActiveTexture(GL_TEXTURE2); + if (m_usefbo) + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + m_glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0); - if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); - pfn_glActiveTexture(GL_TEXTURE1); + if (m_usefbo) + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + m_glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); - if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); - pfn_glActiveTexture(GL_TEXTURE0); + if (m_usefbo) + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + m_glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); - if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + if (m_usefbo) + m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); } glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); @@ -2775,10 +2902,140 @@ void renderer_ogl::texture_all_disable() glDisableClientState(GL_TEXTURE_COORD_ARRAY); if(m_usevbo) { - pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, 0); // unbind .. + m_glBindBuffer( GL_ARRAY_BUFFER_ARB, 0); // unbind .. } if ( m_usepbo ) { - pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); + m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); + } +} + + +class video_opengl : public osd_module, public render_module +{ +public: + video_opengl() + : osd_module(OSD_RENDERER_PROVIDER, "opengl") +#if defined(USE_DISPATCH_GL) + , m_dll_loaded(false) +#endif + { + } + ~video_opengl() { exit(); } + + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override; + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return FLAG_INTERACTIVE | FLAG_SDL_NEEDS_OPENGL; } + +private: + ogl_video_config m_ogl_config; +#if defined(USE_DISPATCH_GL) + bool m_dll_loaded; +#endif +}; + +int video_opengl::init(osd_interface &osd, osd_options const &options) +{ + m_ogl_config.vbo = options.gl_vbo(); + m_ogl_config.pbo = options.gl_pbo(); + m_ogl_config.allowtexturerect = !options.gl_no_texture_rect(); + m_ogl_config.forcepow2texture = options.gl_force_pow2_texture(); + m_ogl_config.glsl = options.gl_glsl(); + if (m_ogl_config.glsl) + { + m_ogl_config.glsl_filter = options.glsl_filter(); + + m_ogl_config.glsl_shader_mamebm_num = 0; + for (int i = 0; i < GLSL_SHADER_MAX; i++) + { + char const *const stemp = options.shader_mame(i); + if (stemp && *stemp && strcmp(stemp, OSDOPTVAL_NONE)) + m_ogl_config.glsl_shader_mamebm[m_ogl_config.glsl_shader_mamebm_num++] = stemp; + } + + m_ogl_config.glsl_shader_scrn_num = 0; + for (int i = 0; i < GLSL_SHADER_MAX; i++) + { + char const *const stemp = options.shader_screen(i); + if (stemp && *stemp && strcmp(stemp, OSDOPTVAL_NONE)) + m_ogl_config.glsl_shader_scrn[m_ogl_config.glsl_shader_scrn_num++] = stemp; + } + } + else + { + m_ogl_config.glsl_filter = 0; + + m_ogl_config.glsl_shader_mamebm_num = 0; + for (std::string &s : m_ogl_config.glsl_shader_mamebm) + s.clear(); + + m_ogl_config.glsl_shader_scrn_num = 0; + for (std::string &s : m_ogl_config.glsl_shader_scrn) + s.clear(); + } + +#if defined(USE_DISPATCH_GL) +#if defined(OSD_SDL) + if (!m_dll_loaded) + { + // directfb and and x11 use this env var: SDL_VIDEO_GL_DRIVER + char const *libname = dynamic_cast<sdl_options const &>(options).gl_lib(); + if (libname && (!*libname || !std::strcmp(libname, OSDOPTVAL_AUTO))) + libname = nullptr; + + if (SDL_GL_LoadLibrary(libname) != 0) + { + osd_printf_error("Unable to load OpenGL shared library: %s\n", libname ? libname : "<default>"); + return -1; + } + + osd_printf_verbose("Loaded OpenGL shared library: %s\n", libname ? libname : "<default>"); } +#endif // defined(OSD_SDL) + m_dll_loaded = true; +#endif // defined(USE_DISPATCH_GL) + +#if defined(OSD_WINDOWS) + osd_printf_verbose("Using Windows OpenGL driver\n"); +#else // defined(OSD_WINDOWS) + osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n"); +#endif // defined(OSD_WINDOWS) + + return 0; +} + +void video_opengl::exit() +{ +#if defined(USE_DISPATCH_GL) +#if defined(OSD_SDL) + if (m_dll_loaded) + SDL_GL_UnloadLibrary(); +#endif // defined(OSD_SDL) + m_dll_loaded = false; +#endif // defined(USE_DISPATCH_GL) + + m_ogl_config = ogl_video_config(); +} + +std::unique_ptr<osd_renderer> video_opengl::create(osd_window &window) +{ + return std::make_unique<renderer_ogl>(window, m_ogl_config); } + +} // anonymous namespace + +} // namespace osd + + +#else // USE_OPENGL + +namespace osd { namespace { MODULE_NOT_SUPPORTED(video_opengl, OSD_RENDERER_PROVIDER, "opengl") } } + +#endif // USE_OPENGL + + +MODULE_DEFINITION(RENDERER_OPENGL, osd::video_opengl) diff --git a/src/osd/modules/render/drawogl.h b/src/osd/modules/render/drawogl.h deleted file mode 100644 index 61b9794353c..00000000000 --- a/src/osd/modules/render/drawogl.h +++ /dev/null @@ -1,244 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Olivier Galibert, R. Belmont -//============================================================ -// -// drawogl.h - SDL software and OpenGL implementation -// -// SDLMAME by Olivier Galibert and R. Belmont -// -//============================================================ -#ifndef MAME_OSD_MODULES_RENDER_DRAWOGL_H -#define MAME_OSD_MODULES_RENDER_DRAWOGL_H - -#pragma once - -// OSD headers -#ifndef OSD_WINDOWS -#ifdef OSD_MAC -#include "osdmac.h" -#else -#include "osdsdl.h" -#endif -#include "window.h" -#else -#include "../windows/window.h" -typedef uint64_t HashT; -#endif - -#if defined(OSD_WINDOWS) -#include "winglcontext.h" -#elif defined (OSD_MAC) -#else -#include "sdlglcontext.h" -#endif - -#include "modules/opengl/gl_shader_mgr.h" - -#include "emucore.h" -#include "render.h" - -//============================================================ -// Textures -//============================================================ - -/* ogl_texture_info holds information about a texture */ -class ogl_texture_info -{ -public: - ogl_texture_info() - : hash(0), flags(0), rawwidth(0), rawheight(0), - rawwidth_create(0), rawheight_create(0), - type(0), format(0), borderpix(0), xprescale(0), yprescale(0), nocopy(0), - texture(0), texTarget(0), texpow2(0), mpass_dest_idx(0), pbo(0), data(nullptr), - data_own(0), texCoordBufferName(0) - { - for (int i=0; i<2; i++) - { - mpass_textureunit[i] = 0; - mpass_texture_mamebm[i] = 0; - mpass_fbo_mamebm[i] = 0; - mpass_texture_scrn[i] = 0; - mpass_fbo_scrn[i] = 0; - } - for (int i=0; i<8; i++) - texCoord[i] = 0.0f; - } - - HashT hash; // hash value for the texture (must be >= pointer size) - uint32_t flags; // rendering flags - render_texinfo texinfo; // copy of the texture info - int rawwidth, rawheight; // raw width/height of the texture - int rawwidth_create; // raw width/height, pow2 compatible, if needed - int rawheight_create; // (create and initial set the texture, not for copy!) - int type; // what type of texture are we? - int format; // texture format - int borderpix; // do we have a 1 pixel border? - int xprescale; // what is our X prescale factor? - int yprescale; // what is our Y prescale factor? - int nocopy; // must the texture date be copied? - - uint32_t texture; // OpenGL texture "name"/ID - - GLenum texTarget; // OpenGL texture target - int texpow2; // Is this texture pow2 - - uint32_t mpass_dest_idx; // Multipass dest idx [0..1] - uint32_t mpass_textureunit[2]; // texture unit names for GLSL - - uint32_t mpass_texture_mamebm[2];// Multipass OpenGL texture "name"/ID for the shader - uint32_t mpass_fbo_mamebm[2]; // framebuffer object for this texture, multipass - uint32_t mpass_texture_scrn[2]; // Multipass OpenGL texture "name"/ID for the shader - uint32_t mpass_fbo_scrn[2]; // framebuffer object for this texture, multipass - - uint32_t pbo; // pixel buffer object for this texture (DYNAMIC only!) - uint32_t *data; // pixels for the texture - int data_own; // do we own / allocated it ? - GLfloat texCoord[8]; - GLuint texCoordBufferName; - -}; - -/* renderer_ogl is the information about OpenGL for the current screen */ -class renderer_ogl : public osd_renderer -{ -public: - renderer_ogl(std::shared_ptr<osd_window> window) - : osd_renderer(window, FLAG_NEEDS_OPENGL) - , m_blittimer(0) - , m_width(0) - , m_height(0) - , m_blit_dim(0, 0) - , m_gl_context(nullptr) - , m_initialized(0) - , m_last_blendmode(0) - , m_texture_max_width(0) - , m_texture_max_height(0) - , m_texpoweroftwo(0) - , m_usevbo(0) - , m_usepbo(0) - , m_usefbo(0) - , m_useglsl(0) - , m_glsl(nullptr) - , m_glsl_program_num(0) - , m_glsl_program_mb2sc(0) - , m_usetexturerect(0) - , m_init_context(0) - , m_last_hofs(0.0f) - , m_last_vofs(0.0f) - , m_surf_w(0) - , m_surf_h(0) - { - for (int i=0; i < HASH_SIZE + OVERFLOW_SIZE; i++) - m_texhash[i] = nullptr; - for (int i=0; i < 2*GLSL_SHADER_MAX; i++) - m_glsl_program[i] = 0; - for (int i=0; i < 8; i++) - m_texVerticex[i] = 0.0f; - } - virtual ~renderer_ogl(); - - static void init(running_machine &machine); - static void exit(); - - virtual int create() override; - virtual int draw(const int update) override; - -#ifndef OSD_WINDOWS - virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; -#endif - virtual render_primitive_list *get_primitives() override - { - auto win = try_getwindow(); - if (win == nullptr) - return nullptr; - - osd_dim nd = win->get_size(); - if (nd != m_blit_dim) - { - m_blit_dim = nd; - notify_changed(); - } - if ((m_blit_dim.width() == 0) || (m_blit_dim.height() == 0)) - return nullptr; - win->target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), win->pixel_aspect()); - return &win->target()->get_primitives(); - } - -#ifdef OSD_WINDOWS - virtual void save() override { } - virtual void record() override { } - virtual void toggle_fsfx() override { } -#endif - -private: - static const uint32_t HASH_SIZE = ((1 << 10) + 1); - static const uint32_t OVERFLOW_SIZE = (1 << 10); - - void destroy_all_textures(); - - static void load_gl_lib(running_machine &machine); - void loadGLExtensions(); - void initialize_gl(); - void set_blendmode(int blendmode); - HashT texture_compute_hash(const render_texinfo *texture, uint32_t flags); - void texture_compute_type_subroutine(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); - void texture_compute_size_subroutine(ogl_texture_info *texture, uint32_t flags, - uint32_t width, uint32_t height, - int* p_width, int* p_height, int* p_width_create, int* p_height_create); - void texture_compute_size_type(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); - ogl_texture_info *texture_create(const render_texinfo *texsource, uint32_t flags); - int texture_shader_create(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); - ogl_texture_info *texture_find(const render_primitive *prim); - void texture_coord_update(ogl_texture_info *texture, const render_primitive *prim, int shaderIdx); - void texture_mpass_flip(ogl_texture_info *texture, int shaderIdx); - void texture_shader_update(ogl_texture_info *texture, render_container *container, int shaderIdx); - ogl_texture_info * texture_update(const render_primitive *prim, int shaderIdx); - void texture_disable(ogl_texture_info * texture); - void texture_all_disable(); - - int32_t m_blittimer; - int m_width; - int m_height; - osd_dim m_blit_dim; - - osd_gl_context *m_gl_context; - - int m_initialized; // is everything well initialized, i.e. all GL stuff etc. - // 3D info (GL mode only) - ogl_texture_info * m_texhash[HASH_SIZE + OVERFLOW_SIZE]; - int m_last_blendmode; // previous blendmode - int32_t m_texture_max_width; // texture maximum width - int32_t m_texture_max_height; // texture maximum height - int m_texpoweroftwo; // must textures be power-of-2 sized? - int m_usevbo; // runtime check if VBO is available - int m_usepbo; // runtime check if PBO is available - int m_usefbo; // runtime check if FBO is available - int m_useglsl; // runtime check if GLSL is available - - glsl_shader_info *m_glsl; // glsl_shader_info - - GLhandleARB m_glsl_program[2*GLSL_SHADER_MAX]; // GLSL programs, or 0 - int m_glsl_program_num; // number of GLSL programs - int m_glsl_program_mb2sc; // GLSL program idx, which transforms - // the mame-bitmap. screen-bitmap (size/rotation/..) - // All progs <= glsl_program_mb2sc using the mame bitmap - // as input, otherwise the screen bitmap. - // All progs >= glsl_program_mb2sc using the screen bitmap - // as output, otherwise the mame bitmap. - int m_usetexturerect; // use ARB_texture_rectangle for non-power-of-2, general use - - int m_init_context; // initialize context before next draw - - float m_last_hofs; - float m_last_vofs; - - // Static vars from draogl_window_dra - int32_t m_surf_w; - int32_t m_surf_h; - GLfloat m_texVerticex[8]; - - static bool s_shown_video_info; - static bool s_dll_loaded; -}; - -#endif // MAME_OSD_MODULES_RENDER_DRAWOGL_H diff --git a/src/osd/modules/render/drawsdl.cpp b/src/osd/modules/render/drawsdl.cpp index 9e5de59c412..b1a9e93f42e 100644 --- a/src/osd/modules/render/drawsdl.cpp +++ b/src/osd/modules/render/drawsdl.cpp @@ -2,7 +2,7 @@ // copyright-holders:Couriersud, Olivier Galibert, R. Belmont //============================================================ // -// drawsdl.c - SDL software and OpenGL implementation +// drawsdl.cpp - SDL software and OpenGL implementation // // SDLMAME by Olivier Galibert and R. Belmont // @@ -10,28 +10,37 @@ // //============================================================ -// standard C headers -#include <cmath> -#include <cstdio> +#include "render_module.h" + +#include "modules/osdmodule.h" + +#if defined(OSD_SDL) + +// from specific OSD implementation +#include "sdlopts.h" +#include "window.h" + +// general OSD headers +#include "modules/monitor/monitor_module.h" // MAME headers #include "emucore.h" -#include "ui/uimain.h" +#include "render.h" #include "rendersw.hxx" -// standard SDL headers +//#include "ui/uimain.h" + #include <SDL2/SDL.h> -// OSD headers -#include "osdsdl.h" -#include "window.h" +// standard C headers +#include <cmath> +#include <cstdio> +#include <memory> -#include "drawsdl.h" -#include "modules/monitor/monitor_module.h" -//============================================================ -// DEBUGGING -//============================================================ +namespace osd { + +namespace { //============================================================ // CONSTANTS @@ -41,60 +50,90 @@ #define DRAW2_SCALEMODE_LINEAR "1" #define DRAW2_SCALEMODE_BEST "2" -//============================================================ -// PROTOTYPES -//============================================================ - -// YUV overlays - -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, \ - 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, \ - 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, \ - const uint32_t *lookup, const int width, const int height); - -// Static declarations -static const sdl_scale_mode scale_modes[] = +struct sdl_scale_mode { - { "none", 0, 0, 1, 1, DRAW2_SCALEMODE_NEAREST, 0, nullptr }, - { "hwblit", 1, 0, 1, 1, DRAW2_SCALEMODE_LINEAR, 0, nullptr }, - { "hwbest", 1, 0, 1, 1, DRAW2_SCALEMODE_BEST, 0, nullptr }, - /* SDL1.2 uses interpolation as well */ - { "yv12", 1, 1, 1, 1, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YV12, yuv_RGB_to_YV12 }, - { "yv12x2", 1, 1, 2, 2, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YV12, yuv_RGB_to_YV12X2 }, - { "yuy2", 1, 1, 1, 1, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YUY2, yuv_RGB_to_YUY2 }, - { "yuy2x2", 1, 1, 2, 1, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YUY2, yuv_RGB_to_YUY2X2 }, - { nullptr } + const char *name; + int is_scale; /* Scale mode? */ + int is_yuv; /* Yuv mode? */ + int mult_w; /* Width multiplier */ + int mult_h; /* Height multiplier */ + const char *sdl_scale_mode_hint; /* what to use as a hint ? */ + int pixel_format; /* Pixel/Overlay format */ + void (*yuv_blit)(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height); }; -int drawsdl_scale_mode(const char *s) -{ - const sdl_scale_mode *sm = scale_modes; - int index; - index = 0; - while (sm->name != nullptr) +// renderer_sdl1 is the information about SDL for the current screen +class renderer_sdl1 : public osd_renderer +{ +public: + + renderer_sdl1(osd_window &w, sdl_scale_mode const &scale_mode) + : osd_renderer(w) + , m_scale_mode(scale_mode) + , m_sdl_renderer(nullptr) + , m_texture_id(nullptr) + , m_yuv_lookup() + , m_yuv_bitmap() + //, m_hw_scale_width(0) + //, m_hw_scale_height(0) + , m_last_hofs(0) + , m_last_vofs(0) + , m_blit_dim(0, 0) + , m_last_dim(0, 0) { - if (strcmp(sm->name, s) == 0) - return index; - index++; - sm++; } - return -1; -} + virtual ~renderer_sdl1(); + + virtual int create() override; + virtual int draw(const int update) override; + virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; + virtual render_primitive_list *get_primitives() override; + +private: + void show_info(struct SDL_RendererInfo *render_info); + + void destroy_all_textures(); + void yuv_init(); + void setup_texture(const osd_dim &size); + void yuv_lookup_set(unsigned int pen, unsigned char red, + unsigned char green, unsigned char blue); + + int32_t m_blittimer; + + sdl_scale_mode const &m_scale_mode; + SDL_Renderer *m_sdl_renderer; + SDL_Texture *m_texture_id; + + // YUV overlay + std::unique_ptr<uint32_t []> m_yuv_lookup; + std::unique_ptr<uint16_t []> m_yuv_bitmap; + + // if we leave scaling to SDL and the underlying driver, this + // is the render_target_width/height to use + + int m_last_hofs; + int m_last_vofs; + osd_dim m_blit_dim; + osd_dim m_last_dim; +}; + //============================================================ -// drawsdl_init +// PROTOTYPES //============================================================ -void renderer_sdl1::init(running_machine &machine) -{ - osd_printf_verbose("Using SDL multi-window soft driver (SDL 2.0+)\n"); -} +// YUV overlays + +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, + 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, + 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, + const uint32_t *lookup, const int width, const int height); //============================================================ // setup_texture for window @@ -102,38 +141,35 @@ void renderer_sdl1::init(running_machine &machine) void renderer_sdl1::setup_texture(const osd_dim &size) { - const sdl_scale_mode *sdl_sm = &scale_modes[video_config.scale_mode]; SDL_DisplayMode mode; uint32_t fmt; - auto win = assert_window(); - // Determine preferred pixelformat and set up yuv if necessary - SDL_GetCurrentDisplayMode(win->monitor()->oshandle(), &mode); + SDL_GetCurrentDisplayMode(window().monitor()->oshandle(), &mode); m_yuv_bitmap.reset(); - fmt = (sdl_sm->pixel_format ? sdl_sm->pixel_format : mode.format); + fmt = (m_scale_mode.pixel_format ? m_scale_mode.pixel_format : mode.format); - if (sdl_sm->is_scale) + if (m_scale_mode.is_scale) { int m_hw_scale_width = 0; int m_hw_scale_height = 0; - win->target()->compute_minimum_size(m_hw_scale_width, m_hw_scale_height); - if (win->prescale()) + window().target()->compute_minimum_size(m_hw_scale_width, m_hw_scale_height); + if (window().prescale()) { - m_hw_scale_width *= win->prescale(); - m_hw_scale_height *= win->prescale(); + m_hw_scale_width *= window().prescale(); + m_hw_scale_height *= window().prescale(); /* This must be a multiple of 2 */ m_hw_scale_width = (m_hw_scale_width + 1) & ~1; } - if (sdl_sm->is_yuv) + if (m_scale_mode.is_yuv) m_yuv_bitmap = std::make_unique<uint16_t []>(m_hw_scale_width * m_hw_scale_height); - int w = m_hw_scale_width * sdl_sm->mult_w; - int h = m_hw_scale_height * sdl_sm->mult_h; + int w = m_hw_scale_width * m_scale_mode.mult_w; + int h = m_hw_scale_height * m_scale_mode.mult_h; m_texture_id = SDL_CreateTexture(m_sdl_renderer, fmt, SDL_TEXTUREACCESS_STREAMING, w, h); @@ -182,26 +218,23 @@ void renderer_sdl1::show_info(struct SDL_RendererInfo *render_info) int renderer_sdl1::create() { - const sdl_scale_mode *sm = &scale_modes[video_config.scale_mode]; - - auto win = assert_window(); // create renderer /* set hints ... */ - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, sm->sdl_scale_mode_hint); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, m_scale_mode.sdl_scale_mode_hint); if (video_config.waitvsync) - m_sdl_renderer = SDL_CreateRenderer(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); + m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); else - m_sdl_renderer = SDL_CreateRenderer(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window(), -1, SDL_RENDERER_ACCELERATED); + m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_ACCELERATED); if (!m_sdl_renderer) { if (video_config.waitvsync) - m_sdl_renderer = SDL_CreateRenderer(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_SOFTWARE); + m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_SOFTWARE); else - m_sdl_renderer = SDL_CreateRenderer(std::dynamic_pointer_cast<sdl_window_info>(win)->platform_window(), -1, SDL_RENDERER_SOFTWARE); + m_sdl_renderer = SDL_CreateRenderer(dynamic_cast<sdl_window_info &>(window()).platform_window(), -1, SDL_RENDERER_SOFTWARE); } if (!m_sdl_renderer) @@ -216,18 +249,18 @@ int renderer_sdl1::create() // Check scale mode - if (sm->pixel_format) + if (m_scale_mode.pixel_format) { int i; int found = 0; for (i=0; i < render_info.num_texture_formats; i++) - if (sm->pixel_format == render_info.texture_formats[i]) + if (m_scale_mode.pixel_format == render_info.texture_formats[i]) found = 1; if (!found) { - fatalerror("window: Scale mode %s not supported!", sm->name); + fatalerror("window: Scale mode %s not supported!", m_scale_mode.name); } } @@ -288,7 +321,6 @@ void renderer_sdl1::destroy_all_textures() int renderer_sdl1::draw(int update) { - const sdl_scale_mode *sm = &scale_modes[video_config.scale_mode]; uint8_t *surfptr; int32_t pitch; Uint32 rmask, gmask, bmask; @@ -296,14 +328,7 @@ int renderer_sdl1::draw(int update) int32_t vofs, hofs, blitwidth, blitheight, ch, cw; int bpp; - if (video_config.novideo) - { - return 0; - } - - auto win = assert_window(); - - osd_dim wdim = win->get_size(); + osd_dim wdim = window().get_size(); if (has_flags(FI_CHANGED) || (wdim != m_last_dim)) { destroy_all_textures(); @@ -370,15 +395,15 @@ int renderer_sdl1::draw(int update) m_last_hofs = hofs; m_last_vofs = vofs; - win->m_primlist->acquire_lock(); + window().m_primlist->acquire_lock(); int mamewidth, mameheight; Uint32 fmt = 0; int access = 0; SDL_QueryTexture(m_texture_id, &fmt, &access, &mamewidth, &mameheight); - mamewidth /= sm->mult_w; - mameheight /= sm->mult_h; + mamewidth /= m_scale_mode.mult_w; + mameheight /= m_scale_mode.mult_h; //printf("w h %d %d %d %d\n", mamewidth, mameheight, blitwidth, blitheight); // rescale bounds @@ -388,7 +413,7 @@ int renderer_sdl1::draw(int update) // FIXME: this could be a lot easier if we get the primlist here! // Bounds would be set fit for purpose and done! - for (render_primitive &prim : *win->m_primlist) + for (render_primitive &prim : *window().m_primlist) { prim.bounds.x0 = floor(fw * prim.bounds.x0 + 0.5f); prim.bounds.x1 = floor(fw * prim.bounds.x1 + 0.5f); @@ -397,32 +422,32 @@ int renderer_sdl1::draw(int update) } // render to it - if (!sm->is_yuv) + if (!m_scale_mode.is_yuv) { switch (rmask) { case 0xff000000: - software_renderer<uint32_t, 0,0,0, 24,16,8>::draw_primitives(*win->m_primlist, surfptr, mamewidth, mameheight, pitch / 4); + software_renderer<uint32_t, 0,0,0, 24,16,8>::draw_primitives(*window().m_primlist, surfptr, mamewidth, mameheight, pitch / 4); break; case 0x0000ff00: - software_renderer<uint32_t, 0,0,0, 8,16,24>::draw_primitives(*win->m_primlist, surfptr, mamewidth, mameheight, pitch / 4); + software_renderer<uint32_t, 0,0,0, 8,16,24>::draw_primitives(*window().m_primlist, surfptr, mamewidth, mameheight, pitch / 4); break; case 0x00ff0000: - software_renderer<uint32_t, 0,0,0, 16,8,0>::draw_primitives(*win->m_primlist, surfptr, mamewidth, mameheight, pitch / 4); + software_renderer<uint32_t, 0,0,0, 16,8,0>::draw_primitives(*window().m_primlist, surfptr, mamewidth, mameheight, pitch / 4); break; case 0x000000ff: - software_renderer<uint32_t, 0,0,0, 0,8,16>::draw_primitives(*win->m_primlist, surfptr, mamewidth, mameheight, pitch / 4); + software_renderer<uint32_t, 0,0,0, 0,8,16>::draw_primitives(*window().m_primlist, surfptr, mamewidth, mameheight, pitch / 4); break; case 0xf800: - software_renderer<uint16_t, 3,2,3, 11,5,0>::draw_primitives(*win->m_primlist, surfptr, mamewidth, mameheight, pitch / 2); + software_renderer<uint16_t, 3,2,3, 11,5,0>::draw_primitives(*window().m_primlist, surfptr, mamewidth, mameheight, pitch / 2); break; case 0x7c00: - software_renderer<uint16_t, 3,3,3, 10,5,0>::draw_primitives(*win->m_primlist, surfptr, mamewidth, mameheight, pitch / 2); + software_renderer<uint16_t, 3,3,3, 10,5,0>::draw_primitives(*window().m_primlist, surfptr, mamewidth, mameheight, pitch / 2); break; default: @@ -434,11 +459,11 @@ int renderer_sdl1::draw(int update) { assert (m_yuv_bitmap != nullptr); assert (surfptr != nullptr); - software_renderer<uint16_t, 3,3,3, 10,5,0>::draw_primitives(*win->m_primlist, m_yuv_bitmap.get(), mamewidth, mameheight, mamewidth); - sm->yuv_blit(m_yuv_bitmap.get(), surfptr, pitch, m_yuv_lookup.get(), mamewidth, mameheight); + software_renderer<uint16_t, 3,3,3, 10,5,0>::draw_primitives(*window().m_primlist, m_yuv_bitmap.get(), mamewidth, mameheight, mamewidth); + m_scale_mode.yuv_blit(m_yuv_bitmap.get(), surfptr, pitch, m_yuv_lookup.get(), mamewidth, mameheight); } - win->m_primlist->release_lock(); + window().m_primlist->release_lock(); // unlock and flip SDL_UnlockTexture(m_texture_id); @@ -527,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; @@ -581,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 */ @@ -616,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 */ @@ -642,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 */ @@ -666,16 +691,98 @@ static void yuv_RGB_to_YUY2X2(const uint16_t *bitmap, uint8_t *ptr, const int pi render_primitive_list *renderer_sdl1::get_primitives() { - auto win = try_getwindow(); - if (win == nullptr) - return nullptr; - - osd_dim nd = win->get_size(); + osd_dim nd = window().get_size(); if (nd != m_blit_dim) { m_blit_dim = nd; notify_changed(); } - win->target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), win->pixel_aspect()); - return &win->target()->get_primitives(); + window().target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), window().pixel_aspect()); + return &window().target()->get_primitives(); +} + + +class video_sdl1 : public osd_module, public render_module +{ +public: + video_sdl1() + : osd_module(OSD_RENDERER_PROVIDER, "soft") + , m_scale_mode(-1) + { + } + + virtual int init(osd_interface &osd, osd_options const &options) override; + virtual void exit() override { } + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) override; + +protected: + virtual unsigned flags() const override { return FLAG_INTERACTIVE; } + +private: + static int get_scale_mode(char const *modestr); + + int m_scale_mode; + + static sdl_scale_mode const s_scale_modes[]; +}; + +int video_sdl1::init(osd_interface &osd, osd_options const &options) +{ + osd_printf_verbose("Using SDL multi-window soft driver (SDL 2.0+)\n"); + + // yuv settings ... + char const *const modestr = dynamic_cast<sdl_options const &>(options).scale_mode(); + m_scale_mode = get_scale_mode(modestr); + if (m_scale_mode < 0) + { + osd_printf_warning("Invalid yuvmode value %s; reverting to none\n", modestr); + m_scale_mode = 0; + } + + return 0; +} + +std::unique_ptr<osd_renderer> video_sdl1::create(osd_window &window) +{ + return std::make_unique<renderer_sdl1>(window, s_scale_modes[m_scale_mode]); } + +int video_sdl1::get_scale_mode(char const *modestr) +{ + const sdl_scale_mode *sm = s_scale_modes; + int index = 0; + while (sm->name) + { + if (!strcmp(sm->name, modestr)) + return index; + index++; + sm++; + } + return -1; +} + +sdl_scale_mode const video_sdl1::s_scale_modes[] = { + { "none", 0, 0, 1, 1, DRAW2_SCALEMODE_NEAREST, 0, nullptr }, + { "hwblit", 1, 0, 1, 1, DRAW2_SCALEMODE_LINEAR, 0, nullptr }, + { "hwbest", 1, 0, 1, 1, DRAW2_SCALEMODE_BEST, 0, nullptr }, + /* SDL1.2 uses interpolation as well */ + { "yv12", 1, 1, 1, 1, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YV12, yuv_RGB_to_YV12 }, + { "yv12x2", 1, 1, 2, 2, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YV12, yuv_RGB_to_YV12X2 }, + { "yuy2", 1, 1, 1, 1, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YUY2, yuv_RGB_to_YUY2 }, + { "yuy2x2", 1, 1, 2, 1, DRAW2_SCALEMODE_BEST, SDL_PIXELFORMAT_YUY2, yuv_RGB_to_YUY2X2 }, + { nullptr } }; + +} // anonymous namespace + +} // namespace osd + + +#else // defined(OSD_SDL) + +namespace osd { namespace { MODULE_NOT_SUPPORTED(video_sdl1, OSD_RENDERER_PROVIDER, "soft") } } + +#endif // defined(OSD_SDL) + + +MODULE_DEFINITION(RENDERER_SDL1, osd::video_sdl1) diff --git a/src/osd/modules/render/drawsdl.h b/src/osd/modules/render/drawsdl.h deleted file mode 100644 index bc8246a632d..00000000000 --- a/src/osd/modules/render/drawsdl.h +++ /dev/null @@ -1,88 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Couriersud, Olivier Galibert, R. Belmont -//============================================================ -// -// drawsdl.h - SDL software and OpenGL implementation -// -// SDLMAME by Olivier Galibert and R. Belmont -// -// yuvmodes by Couriersud -// -//============================================================ - -#ifndef MAME_OSD_MODULES_RENDER_DRAWSDL_H -#define MAME_OSD_MODULES_RENDER_DRAWSDL_H - -#pragma once - -#include <SDL2/SDL.h> - -/* renderer_sdl1 is the information about SDL for the current screen */ -class renderer_sdl1 : public osd_renderer -{ -public: - - renderer_sdl1(std::shared_ptr<osd_window> w, int extra_flags) - : osd_renderer(w, FLAG_NEEDS_OPENGL | extra_flags) - , m_sdl_renderer(nullptr) - , m_texture_id(nullptr) - , m_yuv_lookup() - , m_yuv_bitmap() - //, m_hw_scale_width(0) - //, m_hw_scale_height(0) - , m_last_hofs(0) - , m_last_vofs(0) - , m_blit_dim(0, 0) - , m_last_dim(0, 0) - { - } - virtual ~renderer_sdl1(); - - static void init(running_machine &machine); - static void exit() { } - - virtual int create() override; - virtual int draw(const int update) override; - virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; - virtual render_primitive_list *get_primitives() override; - -private: - void show_info(struct SDL_RendererInfo *render_info); - - void destroy_all_textures(); - void yuv_init(); - void setup_texture(const osd_dim &size); - void yuv_lookup_set(unsigned int pen, unsigned char red, - unsigned char green, unsigned char blue); - - int32_t m_blittimer; - - SDL_Renderer *m_sdl_renderer; - SDL_Texture *m_texture_id; - - // YUV overlay - std::unique_ptr<uint32_t []> m_yuv_lookup; - std::unique_ptr<uint16_t []> m_yuv_bitmap; - - // if we leave scaling to SDL and the underlying driver, this - // is the render_target_width/height to use - - int m_last_hofs; - int m_last_vofs; - osd_dim m_blit_dim; - osd_dim m_last_dim; -}; - -struct sdl_scale_mode -{ - const char *name; - int is_scale; /* Scale mode? */ - int is_yuv; /* Yuv mode? */ - int mult_w; /* Width multiplier */ - int mult_h; /* Height multiplier */ - const char *sdl_scale_mode_hint; /* what to use as a hint ? */ - int pixel_format; /* Pixel/Overlay format */ - void (*yuv_blit)(const uint16_t *bitmap, uint8_t *ptr, const int pitch, const uint32_t *lookup, const int width, const int height); -}; - -#endif // MAME_OSD_MODULES_RENDER_DRAWSDL_H diff --git a/src/osd/modules/render/render_module.h b/src/osd/modules/render/render_module.h new file mode 100644 index 00000000000..b22f35fdb3c --- /dev/null +++ b/src/osd/modules/render/render_module.h @@ -0,0 +1,38 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +#ifndef MAME_OSD_MODULES_RENDER_RENDER_MODULE_H +#define MAME_OSD_MODULES_RENDER_RENDER_MODULE_H + +#pragma once + +#include "osdepend.h" +#include "modules/osdmodule.h" + +#include <memory> + + +#define OSD_RENDERER_PROVIDER "video" + + +class osd_renderer; +class osd_window; + + +class render_module +{ +public: + virtual ~render_module() = default; + + virtual std::unique_ptr<osd_renderer> create(osd_window &window) = 0; + + bool is_interactive() const { return flags() & FLAG_INTERACTIVE; } + bool sdl_needs_opengl() const { return flags() & FLAG_SDL_NEEDS_OPENGL; } + +protected: + static inline constexpr unsigned FLAG_INTERACTIVE = 1; + static inline constexpr unsigned FLAG_SDL_NEEDS_OPENGL = 2; + + virtual unsigned flags() const = 0; +}; + +#endif // MAME_OSD_MODULES_RENDER_RENDER_MODULE_H diff --git a/src/osd/modules/render/sdlglcontext.h b/src/osd/modules/render/sdlglcontext.h index b8ff4d72aa2..d2055060bd6 100644 --- a/src/osd/modules/render/sdlglcontext.h +++ b/src/osd/modules/render/sdlglcontext.h @@ -8,61 +8,76 @@ // //============================================================ +#ifndef MAME_RENDER_SDLGLCONTEXT_H +#define MAME_RENDER_SDLGLCONTEXT_H + #pragma once -#ifndef __SDL_GL_CONTEXT__ -#define __SDL_GL_CONTEXT__ +#include "modules/opengl/osd_opengl.h" + +#include "strformat.h" #include <SDL2/SDL.h> -#include "modules/opengl/osd_opengl.h" + +#include <string> + class sdl_gl_context : public osd_gl_context { public: - sdl_gl_context(SDL_Window *window) : osd_gl_context(), m_context(0), m_window(window) + sdl_gl_context(SDL_Window *window) : m_context(0), m_window(window) { - m_error[0] = 0; m_context = SDL_GL_CreateContext(window); - if (!m_context) + if (!m_context) { - snprintf(m_error,255, "OpenGL not supported on this driver: %s", SDL_GetError()); + try { m_error = util::string_format("OpenGL not supported on this driver: %s", SDL_GetError()); } + catch (...) { m_error.clear(); } } } + virtual ~sdl_gl_context() { - SDL_GL_DeleteContext(m_context); + if (m_context) + SDL_GL_DeleteContext(m_context); } - virtual void MakeCurrent() override + + virtual explicit operator bool() const override + { + return bool(m_context); + } + + virtual void make_current() override { SDL_GL_MakeCurrent(m_window, m_context); } - virtual int SetSwapInterval(const int swap) override + virtual bool set_swap_interval(const int swap) override { - return SDL_GL_SetSwapInterval(swap); + return 0 == SDL_GL_SetSwapInterval(swap); } - virtual const char *LastErrorMsg() override + virtual const char *last_error_message() override { - if (m_error[0] == 0) - return nullptr; + if (!m_error.empty()) + return m_error.c_str(); else - return m_error; + return nullptr; } - virtual void *getProcAddress(const char *proc) override + + virtual void *get_proc_address(const char *proc) override { return SDL_GL_GetProcAddress(proc); } - virtual void SwapBuffer() override + virtual void swap_buffer() override { SDL_GL_SwapWindow(m_window); } private: SDL_GLContext m_context; - SDL_Window *m_window; - char m_error[256]; + SDL_Window *const m_window; + std::string m_error; }; -#endif // __SDL_GL_CONTEXT__ +#endif // MAME_RENDER_SDLGLCONTEXT_H diff --git a/src/osd/modules/render/winglcontext.h b/src/osd/modules/render/winglcontext.h index f0cb1b6af77..48d1462d2f6 100644 --- a/src/osd/modules/render/winglcontext.h +++ b/src/osd/modules/render/winglcontext.h @@ -7,168 +7,197 @@ // SDLMAME by Olivier Galibert and R. Belmont // //============================================================ +#ifndef MAME_RENDER_WINGLCONTEXT_H +#define MAME_RENDER_WINGLCONTEXT_H #pragma once -#ifndef __WIN_GL_CONTEXT__ -#define __WIN_GL_CONTEXT__ - #include "modules/opengl/osd_opengl.h" #include "modules/lib/osdlib.h" -// Typedefs for dynamically loaded functions -typedef PROC (WINAPI *wglGetProcAddress_fn)(LPCSTR); -typedef HGLRC (WINAPI *wglCreateContext_fn)(HDC); -typedef BOOL (WINAPI *wglDeleteContext_fn)(HGLRC); -typedef BOOL (WINAPI *wglMakeCurrent_fn)(HDC, HGLRC); +#include "strconv.h" + +#include <cstring> +#include <string> -typedef const char * (WINAPI *wglGetExtensionsStringEXT_fn)(void); -typedef BOOL (WINAPI *wglSwapIntervalEXT_fn)(int); -typedef int (WINAPI *wglGetSwapIntervalEXT_fn)(void); class win_gl_context : public osd_gl_context { public: - win_gl_context(HWND window) : osd_gl_context(), m_context(0), m_window(nullptr), m_hdc(0) - { - m_error[0] = 0; + using osd_gl_context::get_proc_address; + win_gl_context(HWND window) : m_context(nullptr), m_window(window), m_hdc(nullptr) + { + // open DLL and bind required functions opengl32_dll = osd::dynamic_module::open({ "opengl32.dll" }); - pfn_wglGetProcAddress = opengl32_dll->bind<wglGetProcAddress_fn>("wglGetProcAddress"); pfn_wglCreateContext = opengl32_dll->bind<wglCreateContext_fn>("wglCreateContext"); pfn_wglDeleteContext = opengl32_dll->bind<wglDeleteContext_fn>("wglDeleteContext"); pfn_wglMakeCurrent = opengl32_dll->bind<wglMakeCurrent_fn>("wglMakeCurrent"); + if (!pfn_wglGetProcAddress || !pfn_wglCreateContext || !pfn_wglDeleteContext || !pfn_wglMakeCurrent) + return; - if (pfn_wglGetProcAddress == nullptr || pfn_wglCreateContext == nullptr || - pfn_wglDeleteContext == nullptr || pfn_wglMakeCurrent == nullptr) + m_hdc = GetDC(window); + if (!m_hdc) { + get_last_error_string(); return; } - pfn_wglGetExtensionsStringEXT = (wglGetExtensionsStringEXT_fn)(*pfn_wglGetProcAddress)("wglGetExtensionsStringEXT"); + if (setupPixelFormat()) + { + m_context = (*pfn_wglCreateContext)(m_hdc); + if (!m_context) + get_last_error_string(); + } + if (!m_context) + return; + + (*pfn_wglMakeCurrent)(m_hdc, m_context); + get_proc_address(pfn_wglGetExtensionsStringEXT, "wglGetExtensionsStringEXT"); if (WGLExtensionSupported("WGL_EXT_swap_control")) { - pfn_wglSwapIntervalEXT = (BOOL (WINAPI *) (int)) getProcAddress("wglSwapIntervalEXT"); - pfn_wglGetSwapIntervalEXT = (int (WINAPI *) (void)) getProcAddress("wglGetSwapIntervalEXT"); + get_proc_address(pfn_wglSwapIntervalEXT, "wglSwapIntervalEXT"); + get_proc_address(pfn_wglGetSwapIntervalEXT, "wglGetSwapIntervalEXT"); } else { pfn_wglSwapIntervalEXT = nullptr; pfn_wglGetSwapIntervalEXT = nullptr; } - - m_hdc = GetDC(window); - if (!setupPixelFormat(m_hdc)) - { - m_context = (*pfn_wglCreateContext)(m_hdc); - if (!m_context) - { - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), 0, m_error, 255, nullptr); - return; - } - (*pfn_wglMakeCurrent)(m_hdc, m_context); - } } virtual ~win_gl_context() { - (*pfn_wglDeleteContext)(m_context); - ReleaseDC(m_window, m_hdc); + if (m_context) + (*pfn_wglDeleteContext)(m_context); + + if (m_hdc) + ReleaseDC(m_window, m_hdc); + } + + virtual explicit operator bool() const override + { + return bool(m_context); } - virtual void MakeCurrent() override + virtual void make_current() override { (*pfn_wglMakeCurrent)(m_hdc, m_context); } - virtual const char *LastErrorMsg() override + virtual const char *last_error_message() override { - if (m_error[0] == 0) - return nullptr; + if (!m_error.empty()) + return m_error.c_str(); else - return m_error; + return nullptr; } - virtual void *getProcAddress(const char *proc) override + virtual void *get_proc_address(const char *proc) override { - return (void *)(*pfn_wglGetProcAddress)(proc); + return reinterpret_cast<void *>(uintptr_t((*pfn_wglGetProcAddress)(proc))); } - virtual int SetSwapInterval(const int swap) override + virtual bool set_swap_interval(const int swap) override { - if (pfn_wglSwapIntervalEXT != nullptr) - { - pfn_wglSwapIntervalEXT(swap ? 1 : 0); - } - return 0; + if (!pfn_wglSwapIntervalEXT) + return false; + + pfn_wglSwapIntervalEXT(swap ? 1 : 0); + return true; } - virtual void SwapBuffer() override + virtual void swap_buffer() override { SwapBuffers(m_hdc); //wglSwapLayerBuffers(GetDC(window().m_hwnd), WGL_SWAP_MAIN_PLANE); } private: + // Typedefs for dynamically loaded functions + typedef PROC (WINAPI *wglGetProcAddress_fn)(LPCSTR); + typedef HGLRC (WINAPI *wglCreateContext_fn)(HDC); + typedef BOOL (WINAPI *wglDeleteContext_fn)(HGLRC); + typedef BOOL (WINAPI *wglMakeCurrent_fn)(HDC, HGLRC); - int setupPixelFormat(HDC hDC) + typedef const char * (WINAPI *wglGetExtensionsStringEXT_fn)(); + typedef BOOL (WINAPI *wglSwapIntervalEXT_fn)(int); + typedef int (WINAPI *wglGetSwapIntervalEXT_fn)(); + + bool setupPixelFormat() { PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), /* size */ - 1, /* version */ - PFD_SUPPORT_OPENGL | - PFD_DRAW_TO_WINDOW | - PFD_DOUBLEBUFFER, /* support double-buffering */ - PFD_TYPE_RGBA, /* color type */ - 32, /* prefered color depth */ - 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ - 0, /* no alpha buffer */ - 0, /* alpha bits (ignored) */ - 0, /* no accumulation buffer */ - 0, 0, 0, 0, /* accum bits (ignored) */ - 16, /* depth buffer */ - 0, /* no stencil buffer */ - 0, /* no auxiliary buffers */ - PFD_MAIN_PLANE, /* main layer */ - 0, /* reserved */ - 0, 0, 0, /* no layer, visible, damage masks */ - }; - - int pixelFormat = ChoosePixelFormat(hDC, &pfd); - + sizeof(PIXELFORMATDESCRIPTOR), // size + 1, // version + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW | + PFD_DOUBLEBUFFER, // support double-buffering + PFD_TYPE_RGBA, // color type + 32, // prefered color depth + 0, 0, 0, 0, 0, 0, // color bits (ignored) + 0, // no alpha buffer + 0, // alpha bits (ignored) + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits (ignored) + 16, // depth buffer + 0, // no stencil buffer + 0, // no auxiliary buffers + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0, // no layer, visible, damage masks + }; + + const int pixelFormat = ChoosePixelFormat(m_hdc, &pfd); if (pixelFormat == 0) { - strcpy(m_error, "ChoosePixelFormat failed."); - return 1; + get_last_error_string(); + return false; } - if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) + if (SetPixelFormat(m_hdc, pixelFormat, &pfd) != TRUE) { - strcpy(m_error, "SetPixelFormat failed."); - return 1; + get_last_error_string(); + return false; } - return 0; + + return true; } bool WGLExtensionSupported(const char *extension_name) { - if (pfn_wglGetExtensionsStringEXT == nullptr) + if (!pfn_wglGetExtensionsStringEXT) return false; - // printf("%s\n", pfn_wglGetExtensionsStringEXT()); + return strstr(pfn_wglGetExtensionsStringEXT(), extension_name) != nullptr; + } - if (strstr(pfn_wglGetExtensionsStringEXT(), extension_name) != nullptr) - return true; - else - return false; + void get_last_error_string() + { + LPTSTR buffer = nullptr; + const auto result = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, + nullptr, + GetLastError(), + 0, + LPTSTR(&buffer), + 0, + nullptr); + if (!result || !buffer) + { + m_error.clear(); + return; + } + try { m_error = osd::text::from_tstring(buffer); } + catch (...) { m_error.clear(); } + LocalFree(buffer); } HGLRC m_context; - HWND m_window; + HWND const m_window; HDC m_hdc; - char m_error[256]; + std::string m_error; osd::dynamic_module::ptr opengl32_dll; wglGetProcAddress_fn pfn_wglGetProcAddress; @@ -181,4 +210,4 @@ private: wglGetSwapIntervalEXT_fn pfn_wglGetSwapIntervalEXT; }; -#endif // __WIN_GL_CONTEXT__ +#endif // MAME_RENDER_WINGLCONTEXT_H diff --git a/src/osd/modules/sound/coreaudio_sound.cpp b/src/osd/modules/sound/coreaudio_sound.cpp index dcc74ec5fe9..2cdb9291168 100644 --- a/src/osd/modules/sound/coreaudio_sound.cpp +++ b/src/osd/modules/sound/coreaudio_sound.cpp @@ -8,10 +8,11 @@ #include "sound_module.h" #include "modules/osdmodule.h" -#include "modules/lib/osdobj_common.h" #ifdef SDLMAME_MACOSX +#include "modules/lib/osdobj_common.h" + #include <AvailabilityMacros.h> #include <AudioToolbox/AudioToolbox.h> #include <AudioUnit/AudioUnit.h> @@ -24,16 +25,9 @@ #include <cstring> -#ifdef MAC_OS_X_VERSION_MAX_ALLOWED - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 - -typedef ComponentDescription AudioComponentDescription; - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060 - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED +namespace osd { +namespace { class sound_coreaudio : public osd_module, public sound_module { @@ -43,6 +37,8 @@ public: sound_module(), m_graph(nullptr), m_node_count(0), + m_sample_rate(0), + m_audio_latency(0), m_sample_bytes(0), m_headroom(0), m_buffer_size(0), @@ -59,7 +55,7 @@ public: { } - virtual int init(osd_options const &options) override; + virtual int init(osd_interface &osd, osd_options const &options) override; virtual void exit() override; // sound_module @@ -173,25 +169,29 @@ private: unsigned m_node_count; node_detail m_node_details[EFFECT_COUNT_MAX + 2]; - uint32_t m_sample_bytes; - uint32_t m_headroom; - uint32_t m_buffer_size; + int m_sample_rate; + int m_audio_latency; + uint32_t m_sample_bytes; + uint32_t m_headroom; + uint32_t m_buffer_size; std::unique_ptr<int8_t []> m_buffer; - uint32_t m_playpos; - uint32_t m_writepos; + uint32_t m_playpos; + uint32_t m_writepos; bool m_in_underrun; - int32_t m_scale; + int32_t m_scale; unsigned m_overflows; unsigned m_underflows; }; -int sound_coreaudio::init(const osd_options &options) +int sound_coreaudio::init(osd_interface &osd, const osd_options &options) { OSStatus err; // Don't bother with any of this if sound is disabled - if (sample_rate() == 0) + m_sample_rate = options.sample_rate(); + m_audio_latency = options.audio_latency(); + if (m_sample_rate == 0) return 0; // Create the output graph @@ -201,7 +201,7 @@ int sound_coreaudio::init(const osd_options &options) // Set audio stream format for two-channel native-endian 16-bit packed linear PCM AudioStreamBasicDescription format; - format.mSampleRate = sample_rate(); + format.mSampleRate = m_sample_rate; format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsSignedInteger @@ -226,8 +226,8 @@ int sound_coreaudio::init(const osd_options &options) m_sample_bytes = format.mBytesPerFrame; // Allocate buffer - m_headroom = m_sample_bytes * (clamped_latency() * sample_rate() / 40); - m_buffer_size = m_sample_bytes * std::max<uint32_t>(sample_rate() * (clamped_latency() + 3) / 40, 256U); + m_headroom = m_sample_bytes * (clamped_latency() * m_sample_rate / 40); + m_buffer_size = m_sample_bytes * std::max<uint32_t>(m_sample_rate * (clamped_latency() + 3) / 40, 256U); try { m_buffer = std::make_unique<int8_t []>(m_buffer_size); @@ -292,7 +292,7 @@ void sound_coreaudio::exit() void sound_coreaudio::update_audio_stream(bool is_throttled, int16_t const *buffer, int samples_this_frame) { - if ((sample_rate() == 0) || !m_buffer) + if ((m_sample_rate == 0) || !m_buffer) return; uint32_t const bytes_this_frame = samples_this_frame * m_sample_bytes; @@ -927,16 +927,17 @@ CFPropertyListRef sound_coreaudio::load_property_list(char const *name) const return nullptr; } - CFStringRef msg = nullptr; - CFPropertyListRef const result = CFPropertyListCreateFromXMLData( + CFErrorRef msg = nullptr; + CFPropertyListRef const result = CFPropertyListCreateWithData( nullptr, data, kCFPropertyListImmutable, + nullptr, &msg); CFRelease(data); if ((nullptr == result) || (nullptr != msg)) { - std::unique_ptr<char []> const buf = (nullptr != msg) ? convert_cfstring_to_utf8(msg) : nullptr; + std::unique_ptr<char []> const buf = (nullptr != msg) ? convert_cfstring_to_utf8(CFErrorCopyDescription(msg)) : nullptr; if (nullptr != msg) CFRelease(msg); @@ -1013,8 +1014,14 @@ OSStatus sound_coreaudio::render_callback( return ((sound_coreaudio *)refcon)->render(action_flags, timestamp, bus_number, number_frames, data); } -#else /* SDLMAME_MACOSX */ - MODULE_NOT_SUPPORTED(sound_coreaudio, OSD_SOUND_PROVIDER, "coreaudio") -#endif +} // anonymous namespace + +} // namespace osd + +#else // SDLMAME_MACOSX + +namespace osd { namespace { MODULE_NOT_SUPPORTED(sound_coreaudio, OSD_SOUND_PROVIDER, "coreaudio") } } + +#endif // SDLMAME_MACOSX -MODULE_DEFINITION(SOUND_COREAUDIO, sound_coreaudio) +MODULE_DEFINITION(SOUND_COREAUDIO, osd::sound_coreaudio) diff --git a/src/osd/modules/sound/direct_sound.cpp b/src/osd/modules/sound/direct_sound.cpp index ff4ba567c2c..0e89bb1ac0d 100644 --- a/src/osd/modules/sound/direct_sound.cpp +++ b/src/osd/modules/sound/direct_sound.cpp @@ -12,14 +12,16 @@ #if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) // MAME headers -#include "osdcore.h" -#include "osdepend.h" #include "emuopts.h" +// osd headers +#include "modules/lib/osdobj_common.h" +#include "osdepend.h" +#include "osdcore.h" + #ifdef SDLMAME_WIN32 -#include "../../sdl/osdsdl.h" +#include "sdl/window.h" #include <SDL2/SDL_syswm.h> -#include "../../sdl/window.h" #else #include "winmain.h" #include "window.h" @@ -48,6 +50,8 @@ #define LOG(...) do { if (LOG_SOUND) osd_printf_verbose(__VA_ARGS__); } while(0) +namespace osd { + namespace { class buffer_base @@ -203,10 +207,11 @@ private: class sound_direct_sound : public osd_module, public sound_module { public: - sound_direct_sound() : osd_module(OSD_SOUND_PROVIDER, "dsound"), sound_module(), + m_sample_rate(0), + m_audio_latency(0), m_bytes_per_sample(0), m_primary_buffer(), m_stream_buffer(), @@ -215,9 +220,8 @@ public: m_buffer_overflows(0) { } - virtual ~sound_direct_sound() { } - virtual int init(osd_options const &options) override; + virtual int init(osd_interface &osd, osd_options const &options) override; virtual void exit() override; // sound_module @@ -233,6 +237,10 @@ private: // DirectSound objects Microsoft::WRL::ComPtr<IDirectSound> m_dsound; + // configuration + int m_sample_rate; + int m_audio_latency; + // descriptors and formats uint32_t m_bytes_per_sample; @@ -251,12 +259,16 @@ private: // init //============================================================ -int sound_direct_sound::init(osd_options const &options) +int sound_direct_sound::init(osd_interface &osd, osd_options const &options) { - // attempt to initialize directsound - // don't make it fatal if we can't -- we'll just run without sound - dsound_init(); + m_sample_rate = options.sample_rate(); + m_audio_latency = options.audio_latency(); m_buffer_underflows = m_buffer_overflows = 0; + + // attempt to initialize DirectSound + if (dsound_init() != DS_OK) + return -1; + return 0; } @@ -403,10 +415,14 @@ HRESULT sound_direct_sound::dsound_init() #ifdef SDLMAME_WIN32 SDL_SysWMinfo wminfo; SDL_VERSION(&wminfo.version); - SDL_GetWindowWMInfo(std::dynamic_pointer_cast<sdl_window_info>(osd_common_t::s_window_list.front())->platform_window(), &wminfo); + if (!SDL_GetWindowWMInfo(dynamic_cast<sdl_window_info &>(*osd_common_t::window_list().front()).platform_window(), &wminfo)) + { + result = DSERR_UNSUPPORTED; // just so it has something to return + goto error; + } HWND const window = wminfo.info.win.window; #else // SDLMAME_WIN32 - HWND const window = std::static_pointer_cast<win_window_info>(osd_common_t::s_window_list.front())->platform_window(); + HWND const window = dynamic_cast<win_window_info &>(*osd_common_t::window_list().front()).platform_window(); #endif // SDLMAME_WIN32 result = m_dsound->SetCooperativeLevel(window, DSSCL_PRIORITY); } @@ -419,12 +435,13 @@ HRESULT sound_direct_sound::dsound_init() { // make a format description for what we want WAVEFORMATEX stream_format; - stream_format.wBitsPerSample = 16; stream_format.wFormatTag = WAVE_FORMAT_PCM; stream_format.nChannels = 2; - stream_format.nSamplesPerSec = sample_rate(); + stream_format.nSamplesPerSec = m_sample_rate; + stream_format.wBitsPerSample = 16; stream_format.nBlockAlign = stream_format.wBitsPerSample * stream_format.nChannels / 8; stream_format.nAvgBytesPerSec = stream_format.nSamplesPerSec * stream_format.nBlockAlign; + stream_format.cbSize = 0; // compute the buffer size based on the output sample rate int audio_latency = std::max(m_audio_latency, 1); @@ -554,9 +571,14 @@ void sound_direct_sound::destroy_buffers() } // anonymous namespace +} // namespace osd + #else // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) - MODULE_NOT_SUPPORTED(sound_direct_sound, OSD_SOUND_PROVIDER, "dsound") + +namespace osd { namespace { MODULE_NOT_SUPPORTED(sound_direct_sound, OSD_SOUND_PROVIDER, "dsound") } } + #endif // defined(OSD_WINDOWS) || defined(SDLMAME_WIN32) -MODULE_DEFINITION(SOUND_DSOUND, sound_direct_sound) + +MODULE_DEFINITION(SOUND_DSOUND, osd::sound_direct_sound) diff --git a/src/osd/modules/sound/js_sound.cpp b/src/osd/modules/sound/js_sound.cpp index afcf6ec8f2b..7375d014141 100644 --- a/src/osd/modules/sound/js_sound.cpp +++ b/src/osd/modules/sound/js_sound.cpp @@ -11,7 +11,7 @@ #include "sound_module.h" #include "modules/osdmodule.h" -#if (defined(SDLMAME_EMSCRIPTEN)) +#if defined(SDLMAME_EMSCRIPTEN) #include "emscripten.h" @@ -19,36 +19,40 @@ class sound_js : public osd_module, public sound_module { public: - sound_js() - : osd_module(OSD_SOUND_PROVIDER, "js"), sound_module() + sound_js() : osd_module(OSD_SOUND_PROVIDER, "js"), sound_module() { } virtual ~sound_js() { } - virtual int init(const osd_options &options) { return 0; } + virtual int init(osd_interface &osd, const osd_options &options) { return 0; } virtual void exit() { } // sound_module virtual void update_audio_stream(bool is_throttled, 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); - }, (unsigned int)buffer, samples_this_frame); + EM_ASM_ARGS( + { + // Forward audio stream update on to JS backend implementation. + jsmame_update_audio_stream($0, $1); + }, + (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); + EM_ASM_ARGS( + { + // Forward volume update on to JS backend implementation. + jsmame_set_mastervolume($0); + }, + attenuation); } - }; -#else /* SDLMAME_UNIX */ +#else // SDLMAME_EMSCRIPTEN MODULE_NOT_SUPPORTED(sound_js, OSD_SOUND_PROVIDER, "js") -#endif +#endif // SDLMAME_EMSCRIPTEN MODULE_DEFINITION(SOUND_JS, sound_js) diff --git a/src/osd/modules/sound/js_sound.js b/src/osd/modules/sound/js_sound.js index 5cbcc380c1b..571a5227f2b 100644 --- a/src/osd/modules/sound/js_sound.js +++ b/src/osd/modules/sound/js_sound.js @@ -173,10 +173,11 @@ function tick (event) { start = 0; } } - //Pad with silence if we're underrunning: + //Pad with latest if we're underrunning: + var idx = (index == 0 ? bufferSize : index) - 1; while (index < 4096) { - buffers[0][index] = 0; - buffers[1][index++] = 0; + buffers[0][index] = buffers[0][idx]; + buffers[1][index++] = buffers[1][idx]; } //Deep inside the bowels of vendors bugs, //we're using watchdog for a firefox bug, diff --git a/src/osd/modules/sound/none.cpp b/src/osd/modules/sound/none.cpp index 685cdc884c2..ba1085f436b 100644 --- a/src/osd/modules/sound/none.cpp +++ b/src/osd/modules/sound/none.cpp @@ -2,32 +2,40 @@ // copyright-holders:Miodrag Milanovic /*************************************************************************** - none.c + none.cpp Dummy sound interface. *******************************************************************c********/ #include "sound_module.h" + #include "modules/osdmodule.h" + +namespace osd { + +namespace { + class sound_none : public osd_module, public sound_module { public: - sound_none() - : osd_module(OSD_SOUND_PROVIDER, "none"), sound_module() + sound_none() : osd_module(OSD_SOUND_PROVIDER, "none") { } virtual ~sound_none() { } - virtual int init(const osd_options &options) override { return 0; } + 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 { } - }; -MODULE_DEFINITION(SOUND_NONE, sound_none) +} // anonymous namespace + +} // namespace osd + +MODULE_DEFINITION(SOUND_NONE, osd::sound_none) diff --git a/src/osd/modules/sound/pa_sound.cpp b/src/osd/modules/sound/pa_sound.cpp index 9472f6812cb..d5bb96a25af 100644 --- a/src/osd/modules/sound/pa_sound.cpp +++ b/src/osd/modules/sound/pa_sound.cpp @@ -9,40 +9,45 @@ *******************************************************************c********/ #include "sound_module.h" + #include "modules/osdmodule.h" #ifndef NO_USE_PORTAUDIO -#include <portaudio.h> #include "modules/lib/osdobj_common.h" +#include "osdcore.h" -#include <iostream> -#include <fstream> -#include <sstream> +#include <portaudio.h> + +#include <algorithm> #include <atomic> -#include <cmath> #include <climits> -#include <algorithm> +#include <cmath> +#include <fstream> +#include <iostream> +#include <sstream> #ifdef _WIN32 #include "pa_win_wasapi.h" #endif + +namespace osd { + +namespace { + #define LOG_FILE "pa.log" #define LOG_BUFCNT 0 -using osd::s16; - class sound_pa : public osd_module, public sound_module { public: - sound_pa() - : osd_module(OSD_SOUND_PROVIDER, "portaudio"), sound_module() + sound_pa() : osd_module(OSD_SOUND_PROVIDER, "portaudio") { } virtual ~sound_pa() { } - virtual int init(osd_options const &options) override; + virtual int init(osd_interface &osd, osd_options const &options) override; virtual void exit() override; // sound_module @@ -152,6 +157,8 @@ private: PaStream* m_pa_stream; PaError err; + int m_sample_rate; + int m_audio_latency; int m_attenuation; audio_buffer<s16>* m_ab; @@ -172,8 +179,12 @@ private: #endif }; -int sound_pa::init(osd_options const &options) +int sound_pa::init(osd_interface &osd, osd_options const &options) { + m_sample_rate = options.sample_rate(); + if (!m_sample_rate) + return 0; + PaStreamParameters stream_params; const PaStreamInfo* stream_info; const PaHostApiInfo* api_info; @@ -182,9 +193,6 @@ int sound_pa::init(osd_options const &options) unsigned long frames_per_callback = paFramesPerBufferUnspecified; double callback_interval; - if (!sample_rate()) - return 0; - m_attenuation = options.volume(); m_underflows = 0; m_overflows = 0; @@ -194,7 +202,7 @@ int sound_pa::init(osd_options const &options) m_skip_threshold_ticks = 0; m_osd_tps = osd_ticks_per_second(); m_buffer_min_ct = INT_MAX; - m_audio_latency = std::clamp<int>(m_audio_latency, LATENCY_MIN, LATENCY_MAX); + m_audio_latency = std::clamp<int>(options.audio_latency(), LATENCY_MIN, LATENCY_MAX); try { m_ab = new audio_buffer<s16>(m_sample_rate, 2); @@ -366,7 +374,7 @@ int sound_pa::callback(s16* output_buffer, size_t number_of_samples) int adjust = m_buffer_min_ct - m_skip_threshold / 2; // if adjustment is less than two milliseconds, don't bother - if (adjust / 2 > sample_rate() / 500) { + if (adjust / 2 > m_sample_rate / 500) { m_ab->increment_playpos(adjust); m_has_overflowed = true; } @@ -393,7 +401,7 @@ int sound_pa::callback(s16* output_buffer, size_t number_of_samples) void sound_pa::update_audio_stream(bool is_throttled, const s16 *buffer, int samples_this_frame) { - if (!sample_rate()) + if (!m_sample_rate) return; #if LOG_BUFCNT @@ -428,7 +436,7 @@ void sound_pa::set_mastervolume(int attenuation) void sound_pa::exit() { - if (!sample_rate()) + if (!m_sample_rate) return; #if LOG_BUFCNT @@ -458,8 +466,14 @@ void sound_pa::exit() osd_printf_verbose("Sound: overflows=%d underflows=%d\n", m_overflows, m_underflows); } +} // anonymous namespace + +} // namespace osd + #else - MODULE_NOT_SUPPORTED(sound_pa, OSD_SOUND_PROVIDER, "portaudio") + +namespace osd { namespace { MODULE_NOT_SUPPORTED(sound_pa, OSD_SOUND_PROVIDER, "portaudio") } } + #endif -MODULE_DEFINITION(SOUND_PORTAUDIO, sound_pa) +MODULE_DEFINITION(SOUND_PORTAUDIO, osd::sound_pa) diff --git a/src/osd/modules/sound/pulse_sound.cpp b/src/osd/modules/sound/pulse_sound.cpp index e9e6ea4d428..373913f9f91 100644 --- a/src/osd/modules/sound/pulse_sound.cpp +++ b/src/osd/modules/sound/pulse_sound.cpp @@ -38,7 +38,7 @@ public: } virtual ~sound_pulse() { } - virtual int init(osd_options const &options) override; + 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; @@ -120,7 +120,7 @@ char sound_pulse::get_main() { pollfd pfds[1]; pfds[0].fd = m_pipe_to_main[0]; - pfds[0].events = POLL_IN; + pfds[0].events = POLLIN; pfds[0].revents = 0; int err = poll(pfds, 1, -1); if(err < 0) @@ -273,7 +273,7 @@ void sound_pulse::stop_mainloop(int err) pa_mainloop_quit(m_mainloop, err); } -int sound_pulse::init(osd_options const &options) +int sound_pulse::init(osd_interface &osd, osd_options const &options) { m_last_sample = 0; m_setting_volume = false; @@ -296,24 +296,26 @@ int sound_pulse::init(osd_options const &options) if(res != 'r') return 1; + const int sample_rate = options.sample_rate(); + pa_sample_spec ss; #ifdef LSB_FIRST ss.format = PA_SAMPLE_S16LE; #else ss.format = PA_SAMPLE_S16BE; #endif - ss.rate = sample_rate(); + 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); pa_buffer_attr battr; - battr.fragsize = sample_rate() / 1000; + battr.fragsize = sample_rate / 1000; battr.maxlength = uint32_t(-1); - battr.minreq = sample_rate() / 1000; + battr.minreq = sample_rate / 1000; battr.prebuf = uint32_t(-1); - battr.tlength = sample_rate() / 1000; + battr.tlength = sample_rate / 1000; err = pa_stream_connect_playback(m_stream, nullptr, &battr, PA_STREAM_ADJUST_LATENCY, nullptr, nullptr); if(err) diff --git a/src/osd/modules/sound/sdl_sound.cpp b/src/osd/modules/sound/sdl_sound.cpp index 9b65a6c176b..ba7082312ea 100644 --- a/src/osd/modules/sound/sdl_sound.cpp +++ b/src/osd/modules/sound/sdl_sound.cpp @@ -9,28 +9,34 @@ //============================================================ #include "sound_module.h" + #include "modules/osdmodule.h" #if (defined(OSD_SDL) || defined(USE_SDL_SOUND)) -// standard sdl header -#include <SDL2/SDL.h> - -// MAME headers -#include "emuopts.h" +#include "modules/lib/osdobj_common.h" #include "osdcore.h" -#include "../../sdl/osdsdl.h" +// standard sdl header +#include <SDL2/SDL.h> #include <algorithm> #include <fstream> #include <memory> + +namespace osd { + +namespace { + //============================================================ // DEBUGGING //============================================================ -#define LOG_SOUND 0 +#define LOG_SOUND 0 + +#define SDLMAME_SOUND_LOG "sound.log" + //============================================================ // CLASS @@ -41,18 +47,24 @@ class sound_sdl : public osd_module, public sound_module public: // number of samples per SDL callback - static const int SDL_XFER_SAMPLES = 512; + 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) -{ - sdl_xfer_samples = SDL_XFER_SAMPLES; + attenuation(0), + buf_locked(0), + stream_buffer(nullptr), + stream_buffer_size(0), + buffer_underflows(0), + buffer_overflows(0) + { } virtual ~sound_sdl() { } - virtual int init(const osd_options &options) override; + virtual int init(osd_interface &osd, const osd_options &options) override; virtual void exit() override; // sound_module @@ -86,6 +98,7 @@ private: int sdl_create_buffers(); void sdl_destroy_buffers(); + int sample_rate; int sdl_xfer_samples; int stream_in_initialized; int attenuation; @@ -239,10 +252,9 @@ void sound_sdl::copy_sample_data(bool is_throttled, const int16_t *data, int byt void sound_sdl::update_audio_stream(bool is_throttled, const int16_t *buffer, int samples_this_frame) { // if nothing to do, don't do it - if (sample_rate() == 0 || !stream_buffer) + if (sample_rate == 0 || !stream_buffer) return; - if (!stream_in_initialized) { // Fill in some zeros to prevent an initial buffer underflow @@ -330,7 +342,7 @@ void sound_sdl::sdl_callback(void *userdata, Uint8 *stream, int len) // sound_sdl::init //============================================================ -int sound_sdl::init(const osd_options &options) +int sound_sdl::init(osd_interface &osd, const osd_options &options) { int n_channels = 2; int audio_latency; @@ -340,7 +352,8 @@ int sound_sdl::init(const osd_options &options) sound_log = std::make_unique<std::ofstream>(SDLMAME_SOUND_LOG); // skip if sound disabled - if (sample_rate() != 0) + sample_rate = options.sample_rate(); + if (sample_rate != 0) { if (SDL_InitSubSystem(SDL_INIT_AUDIO)) { @@ -356,7 +369,7 @@ int sound_sdl::init(const osd_options &options) stream_in_initialized = 0; // set up the audio specs - aspec.freq = sample_rate(); + aspec.freq = sample_rate; aspec.format = AUDIO_S16SYS; // keep endian independent aspec.channels = n_channels; aspec.samples = sdl_xfer_samples; @@ -372,10 +385,10 @@ int sound_sdl::init(const osd_options &options) sdl_xfer_samples = obtained.samples; // pin audio latency - audio_latency = std::clamp(m_audio_latency, 1, MAX_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 = (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; @@ -409,7 +422,7 @@ int sound_sdl::init(const osd_options &options) void sound_sdl::exit() { // if nothing to do, don't do it - if (sample_rate() == 0) + if (sample_rate == 0) return; osd_printf_verbose("sdl_kill: closing audio\n"); @@ -456,10 +469,15 @@ void sound_sdl::sdl_destroy_buffers() stream_buffer.reset(); } +} // anonymous namespace + +} // namespace osd + + +#else // (defined(OSD_SDL) || defined(USE_SDL_SOUND)) +namespace osd { namespace { MODULE_NOT_SUPPORTED(sound_sdl, OSD_SOUND_PROVIDER, "sdl") } } -#else /* SDLMAME_UNIX */ - MODULE_NOT_SUPPORTED(sound_sdl, OSD_SOUND_PROVIDER, "sdl") #endif -MODULE_DEFINITION(SOUND_SDL, sound_sdl) +MODULE_DEFINITION(SOUND_SDL, osd::sound_sdl) diff --git a/src/osd/modules/sound/sound_module.h b/src/osd/modules/sound/sound_module.h index b72f50f7c8c..088da5ea8d0 100644 --- a/src/osd/modules/sound/sound_module.h +++ b/src/osd/modules/sound/sound_module.h @@ -4,9 +4,10 @@ * sound_module.h * */ +#ifndef MAME_OSD_SOUND_SOUND_MODULE_H +#define MAME_OSD_SOUND_SOUND_MODULE_H -#ifndef SOUND_MODULE_H_ -#define SOUND_MODULE_H_ +#pragma once #include <cstdint> @@ -19,17 +20,10 @@ class sound_module { public: - sound_module() : m_sample_rate(0), m_audio_latency(1) { } - - virtual ~sound_module() { } + 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; - - int sample_rate() const { return m_sample_rate; } - - int m_sample_rate; - int m_audio_latency; }; -#endif /* FONT_MODULE_H_ */ +#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 2b797e7ec8f..d0243ba1267 100644 --- a/src/osd/modules/sound/xaudio2_sound.cpp +++ b/src/osd/modules/sound/xaudio2_sound.cpp @@ -7,19 +7,17 @@ //==================================================================== #include "sound_module.h" -#include "modules/osdmodule.h" - -#if defined(OSD_WINDOWS) - -// standard windows headers -#include <windows.h> -#include <wrl/client.h> +#include "modules/osdmodule.h" -// XAudio2 include -#include <xaudio2.h> +#if defined(OSD_WINDOWS) | defined(SDLMAME_WIN32) -#undef interface +// OSD headers +#include "modules/lib/osdlib.h" +#include "modules/lib/osdobj_common.h" +#include "osdcore.h" +#include "osdepend.h" +#include "windows/winutil.h" // stdlib includes #include <algorithm> @@ -28,13 +26,18 @@ #include <queue> #include <thread> -// MAME headers -#include "osdcore.h" -#include "osdepend.h" +// standard windows headers +#include <windows.h> -#include "winutil.h" +#include <wrl/client.h> + +// XAudio2 include +#include <xaudio2.h> -#include "modules/lib/osdlib.h" + +namespace osd { + +namespace { //============================================================ // Constants @@ -181,29 +184,6 @@ public: // The main class for the XAudio2 sound module implementation class sound_xaudio2 : public osd_module, public sound_module, public IXAudio2VoiceCallback { -private: - Microsoft::WRL::ComPtr<IXAudio2> m_xAudio2; - mastering_voice_ptr m_masterVoice; - src_voice_ptr m_sourceVoice; - DWORD m_sample_bytes; - std::unique_ptr<BYTE[]> m_buffer; - DWORD m_buffer_size; - DWORD m_buffer_count; - DWORD m_writepos; - std::mutex m_buffer_lock; - HANDLE m_hEventBufferCompleted; - HANDLE m_hEventDataAvailable; - HANDLE m_hEventExiting; - std::thread m_audioThread; - std::queue<xaudio2_buffer> m_queue; - std::unique_ptr<bufferpool> m_buffer_pool; - uint32_t m_overflows; - uint32_t m_underflows; - BOOL m_in_underflow; - BOOL m_initialized; - OSD_DYNAMIC_API(xaudio2, "XAudio2_9.dll", "XAudio2_8.dll"); - OSD_DYNAMIC_API_FN(xaudio2, HRESULT, WINAPI, XAudio2Create, IXAudio2 **, uint32_t, XAUDIO2_PROCESSOR); - public: sound_xaudio2() : osd_module(OSD_SOUND_PROVIDER, "xaudio2"), @@ -211,6 +191,8 @@ public: m_xAudio2(nullptr), m_masterVoice(nullptr), m_sourceVoice(nullptr), + m_sample_rate(0), + m_audio_latency(0), m_sample_bytes(0), m_buffer(nullptr), m_buffer_size(0), @@ -227,26 +209,24 @@ public: { } - virtual ~sound_xaudio2() { } - bool probe() override; - int init(osd_options const &options) override; + int init(osd_interface &osd, osd_options const &options) override; 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; +private: // Xaudio callbacks - void STDAPICALLTYPE OnVoiceProcessingPassStart(uint32_t bytes_required) override; - void STDAPICALLTYPE OnVoiceProcessingPassEnd() override {} - void STDAPICALLTYPE OnStreamEnd() override {} - void STDAPICALLTYPE OnBufferStart(void* pBufferContext) override {} - void STDAPICALLTYPE OnLoopEnd(void* pBufferContext) override {} - void STDAPICALLTYPE OnVoiceError(void* pBufferContext, HRESULT error) override {} - void STDAPICALLTYPE OnBufferEnd(void *pBufferContext) override; + void STDAPICALLTYPE OnVoiceProcessingPassStart(uint32_t bytes_required) noexcept override; + void STDAPICALLTYPE OnVoiceProcessingPassEnd() noexcept override {} + void STDAPICALLTYPE OnStreamEnd() noexcept override {} + void STDAPICALLTYPE OnBufferStart(void* pBufferContext) noexcept override {} + void STDAPICALLTYPE OnLoopEnd(void* pBufferContext) noexcept override {} + void STDAPICALLTYPE OnVoiceError(void* pBufferContext, HRESULT error) noexcept override {} + void STDAPICALLTYPE OnBufferEnd(void *pBufferContext) noexcept override; -private: void create_buffers(const WAVEFORMATEX &format); HRESULT create_voices(const WAVEFORMATEX &format); void process_audio(); @@ -254,6 +234,31 @@ private: void submit_needed(); void roll_buffer(); BOOL submit_next_queued(); + + Microsoft::WRL::ComPtr<IXAudio2> m_xAudio2; + mastering_voice_ptr m_masterVoice; + src_voice_ptr m_sourceVoice; + int m_sample_rate; + int m_audio_latency; + DWORD m_sample_bytes; + std::unique_ptr<BYTE[]> m_buffer; + DWORD m_buffer_size; + DWORD m_buffer_count; + DWORD m_writepos; + std::mutex m_buffer_lock; + HANDLE m_hEventBufferCompleted; + HANDLE m_hEventDataAvailable; + HANDLE m_hEventExiting; + std::thread m_audioThread; + std::queue<xaudio2_buffer> m_queue; + std::unique_ptr<bufferpool> m_buffer_pool; + uint32_t m_overflows; + uint32_t m_underflows; + BOOL m_in_underflow; + BOOL m_initialized; + + OSD_DYNAMIC_API(xaudio2, "XAudio2_9.dll", "XAudio2_8.dll"); + OSD_DYNAMIC_API_FN(xaudio2, HRESULT, WINAPI, XAudio2Create, IXAudio2 **, uint32_t, XAUDIO2_PROCESSOR); }; //============================================================ @@ -269,14 +274,9 @@ bool sound_xaudio2::probe() // init //============================================================ -int sound_xaudio2::init(osd_options const &options) +int sound_xaudio2::init(osd_interface &osd, osd_options const &options) { - HRESULT result; - WAVEFORMATEX format = {0}; - auto init_start = std::chrono::system_clock::now(); - std::chrono::milliseconds init_time; - - CoInitializeEx(nullptr, COINIT_MULTITHREADED); + auto const init_start = std::chrono::system_clock::now(); // Make sure our XAudio2Create entrypoint is bound if (!OSD_DYNAMIC_API_TEST(XAudio2Create)) @@ -285,6 +285,13 @@ int sound_xaudio2::init(osd_options const &options) return 1; } + HRESULT result; + std::chrono::milliseconds init_time; + WAVEFORMATEX format = { 0 }; + + m_sample_rate = options.sample_rate(); + m_audio_latency = options.audio_latency(); + // Create the IXAudio2 object HR_GOERR(OSD_DYNAMIC_CALL(XAudio2Create, m_xAudio2.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR)); @@ -292,7 +299,7 @@ int sound_xaudio2::init(osd_options const &options) format.wBitsPerSample = 16; format.wFormatTag = WAVE_FORMAT_PCM; format.nChannels = 2; - format.nSamplesPerSec = sample_rate(); + format.nSamplesPerSec = m_sample_rate; format.nBlockAlign = format.wBitsPerSample * format.nChannels / 8; format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; @@ -311,10 +318,10 @@ int sound_xaudio2::init(osd_options const &options) HR_GOERR(m_sourceVoice->Start()); // Start the thread listening - m_audioThread = std::thread([](sound_xaudio2* self) { self->process_audio(); }, this); + m_audioThread = std::thread([] (sound_xaudio2 *self) { self->process_audio(); }, this); init_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - init_start); - osd_printf_verbose("Sound: XAudio2 initialized. %d ms.\n", static_cast<int>(init_time.count())); + osd_printf_verbose("Sound: XAudio2 initialized. %d ms.\n", init_time.count()); m_initialized = TRUE; return 0; @@ -377,7 +384,7 @@ void sound_xaudio2::update_audio_stream( int16_t const *buffer, int samples_this_frame) { - if (!m_initialized || sample_rate() == 0 || !m_buffer) + if (!m_initialized || m_sample_rate == 0 || !m_buffer) return; uint32_t const bytes_this_frame = samples_this_frame * m_sample_bytes; @@ -437,7 +444,7 @@ void sound_xaudio2::set_mastervolume(int attenuation) //============================================================ // The XAudio2 voice callback triggered when a buffer finishes playing -void sound_xaudio2::OnBufferEnd(void *pBufferContext) +void sound_xaudio2::OnBufferEnd(void *pBufferContext) noexcept { BYTE* completed_buffer = static_cast<BYTE*>(pBufferContext); if (completed_buffer != nullptr) @@ -454,7 +461,7 @@ void sound_xaudio2::OnBufferEnd(void *pBufferContext) //============================================================ // The XAudio2 voice callback triggered on every pass -void sound_xaudio2::OnVoiceProcessingPassStart(uint32_t bytes_required) +void sound_xaudio2::OnVoiceProcessingPassStart(uint32_t bytes_required) noexcept { if (bytes_required == 0) { @@ -528,7 +535,7 @@ HRESULT sound_xaudio2::create_voices(const WAVEFORMATEX &format) m_xAudio2->CreateMasteringVoice( &temp_master_voice, format.nChannels, - sample_rate())); + m_sample_rate)); m_masterVoice = mastering_voice_ptr(temp_master_voice); @@ -695,9 +702,15 @@ void sound_xaudio2::roll_buffer() } } +} // anonymous namespace + +} // namespace osd + #else -MODULE_NOT_SUPPORTED(sound_xaudio2, OSD_SOUND_PROVIDER, "xaudio2") + +namespace osd { namespace { MODULE_NOT_SUPPORTED(sound_xaudio2, OSD_SOUND_PROVIDER, "xaudio2") } } + #endif -MODULE_DEFINITION(SOUND_XAUDIO2, sound_xaudio2) +MODULE_DEFINITION(SOUND_XAUDIO2, osd::sound_xaudio2) |