mirror of
https://github.com/holub/mame
synced 2025-06-26 22:29:10 +03:00
Merge pull request #2813 from JoakimLarsson/prodigy_webinterface_1
Prodigy webinterface poc
This commit is contained in:
commit
4f59d001e9
@ -2668,6 +2668,9 @@ files {
|
|||||||
MAME_DIR .. "src/mame/drivers/prodigy.cpp",
|
MAME_DIR .. "src/mame/drivers/prodigy.cpp",
|
||||||
MAME_DIR .. "src/mame/machine/nl_prodigy.cpp",
|
MAME_DIR .. "src/mame/machine/nl_prodigy.cpp",
|
||||||
MAME_DIR .. "src/mame/machine/nl_prodigy.h",
|
MAME_DIR .. "src/mame/machine/nl_prodigy.h",
|
||||||
|
}
|
||||||
|
includedirs {
|
||||||
|
MAME_DIR .. "3rdparty/rapidjson/include",
|
||||||
}
|
}
|
||||||
|
|
||||||
createMESSProjects(_target, _subtarget, "psion")
|
createMESSProjects(_target, _subtarget, "psion")
|
||||||
|
@ -48,6 +48,7 @@ const static struct mapping
|
|||||||
{ "jpx", "image/jpx" },
|
{ "jpx", "image/jpx" },
|
||||||
{ "js", "application/javascript" },
|
{ "js", "application/javascript" },
|
||||||
{ "json", "application/json" },
|
{ "json", "application/json" },
|
||||||
|
{ "lay", "text/xml" },
|
||||||
{ "m3u", "audio/x-mpegurl" },
|
{ "m3u", "audio/x-mpegurl" },
|
||||||
{ "m4v", "video/x-m4v" },
|
{ "m4v", "video/x-m4v" },
|
||||||
{ "mid", "audio/x-midi" },
|
{ "mid", "audio/x-midi" },
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
- Sound
|
- Sound
|
||||||
- Row/column LEDs
|
- Row/column LEDs
|
||||||
- Chess board sensors
|
- Chess board sensors
|
||||||
- Support for SVG/CSS based browser UI
|
|
||||||
|
|
||||||
+-------------------------------------------------------------------------------------+
|
+-------------------------------------------------------------------------------------+
|
||||||
|LEDS--------------------------------------------+ +-----------------+ |
|
|LEDS--------------------------------------------+ +-----------------+ |
|
||||||
@ -68,12 +67,25 @@
|
|||||||
*******************************************************************************************/
|
*******************************************************************************************/
|
||||||
|
|
||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
|
#include "emuopts.h"
|
||||||
#include "cpu/m6502/m6502.h"
|
#include "cpu/m6502/m6502.h"
|
||||||
#include "machine/74145.h"
|
#include "machine/74145.h"
|
||||||
#include "machine/netlist.h"
|
#include "machine/netlist.h"
|
||||||
#include "machine/nl_prodigy.h"
|
#include "machine/nl_prodigy.h"
|
||||||
#include "machine/6522via.h"
|
#include "machine/6522via.h"
|
||||||
|
|
||||||
|
// TODO: Move all HTTPUI stuff global and generic
|
||||||
|
#define HTTPUI 1
|
||||||
|
|
||||||
|
#if HTTPUI
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include "rapidjson/include/rapidjson/document.h"
|
||||||
|
#include "rapidjson/include/rapidjson/stringbuffer.h"
|
||||||
|
#include "rapidjson/include/rapidjson/writer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Generated artwork includes
|
// Generated artwork includes
|
||||||
#include "prodigy.lh"
|
#include "prodigy.lh"
|
||||||
|
|
||||||
@ -84,8 +96,10 @@
|
|||||||
#define LOG_CLK (1U << 5)
|
#define LOG_CLK (1U << 5)
|
||||||
#define LOG_KBD (1U << 6)
|
#define LOG_KBD (1U << 6)
|
||||||
#define LOG_AW (1U << 7)
|
#define LOG_AW (1U << 7)
|
||||||
|
#define LOG_HTTP (1U << 8)
|
||||||
|
#define LOG_XML (1U << 9)
|
||||||
|
|
||||||
//#define VERBOSE (LOG_KBD|LOG_AW) // (LOG_BCD|LOG_NETLIST|LOG_SETUP)
|
//#define VERBOSE (LOG_HTTP|LOG_AW) // (LOG_BCD|LOG_NETLIST|LOG_SETUP)
|
||||||
//#define LOG_OUTPUT_STREAM std::cout
|
//#define LOG_OUTPUT_STREAM std::cout
|
||||||
|
|
||||||
#include "logmacro.h"
|
#include "logmacro.h"
|
||||||
@ -97,6 +111,8 @@
|
|||||||
#define LOGCLK(...) LOGMASKED(LOG_CLK, __VA_ARGS__)
|
#define LOGCLK(...) LOGMASKED(LOG_CLK, __VA_ARGS__)
|
||||||
#define LOGKBD(...) LOGMASKED(LOG_KBD, __VA_ARGS__)
|
#define LOGKBD(...) LOGMASKED(LOG_KBD, __VA_ARGS__)
|
||||||
#define LOGAW(...) LOGMASKED(LOG_AW, __VA_ARGS__)
|
#define LOGAW(...) LOGMASKED(LOG_AW, __VA_ARGS__)
|
||||||
|
#define LOGHTTP(...) LOGMASKED(LOG_HTTP, __VA_ARGS__)
|
||||||
|
#define LOGXML(...) LOGMASKED(LOG_XML, __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define FUNCNAME __func__
|
#define FUNCNAME __func__
|
||||||
@ -114,12 +130,21 @@ public:
|
|||||||
, m_maincpu(*this, "maincpu")
|
, m_maincpu(*this, "maincpu")
|
||||||
, m_74145(*this, "io_74145")
|
, m_74145(*this, "io_74145")
|
||||||
, m_segments(0)
|
, m_segments(0)
|
||||||
|
, m_digit_cache{0xff, 0xff, 0xff, 0xff}
|
||||||
, m_via(*this, "via")
|
, m_via(*this, "via")
|
||||||
, m_bcd(*this, NETLIST_TAG)
|
, m_bcd(*this, NETLIST_TAG)
|
||||||
, m_cb1(*this, "bcd:cb1")
|
, m_cb1(*this, "bcd:cb1")
|
||||||
, m_cb2(*this, "bcd:cb2")
|
, m_cb2(*this, "bcd:cb2")
|
||||||
, m_digit(0.0)
|
, m_digit(0.0)
|
||||||
, m_io_line(*this, "LINE%u", 0)
|
, m_io_line(*this, "LINE%u", 0)
|
||||||
|
#if HTTPUI
|
||||||
|
, m_connection(NULL)
|
||||||
|
, m_web_line0(0)
|
||||||
|
, m_web_line1(0)
|
||||||
|
, m_web_line2(0)
|
||||||
|
, m_web_line3(0)
|
||||||
|
, m_web_line4(0)
|
||||||
|
#endif
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
NETDEV_LOGIC_CALLBACK_MEMBER(bcd_bit0_cb);
|
NETDEV_LOGIC_CALLBACK_MEMBER(bcd_bit0_cb);
|
||||||
@ -143,6 +168,7 @@ private:
|
|||||||
required_device<cpu_device> m_maincpu;
|
required_device<cpu_device> m_maincpu;
|
||||||
required_device<ttl74145_device> m_74145;
|
required_device<ttl74145_device> m_74145;
|
||||||
uint8_t m_segments;
|
uint8_t m_segments;
|
||||||
|
uint8_t m_digit_cache[4];
|
||||||
required_device<via6522_device> m_via;
|
required_device<via6522_device> m_via;
|
||||||
required_device<netlist_mame_device> m_bcd;
|
required_device<netlist_mame_device> m_bcd;
|
||||||
required_device<netlist_mame_logic_input_device> m_cb1;
|
required_device<netlist_mame_logic_input_device> m_cb1;
|
||||||
@ -153,6 +179,24 @@ private:
|
|||||||
virtual void device_start() override;
|
virtual void device_start() override;
|
||||||
required_ioport_array<5> m_io_line;
|
required_ioport_array<5> m_io_line;
|
||||||
uint16_t m_line[5];
|
uint16_t m_line[5];
|
||||||
|
|
||||||
|
#if HTTPUI
|
||||||
|
http_manager *m_server;
|
||||||
|
void on_update(http_manager::http_request_ptr request, http_manager::http_response_ptr response);
|
||||||
|
const std::string decompress_layout_data(const internal_layout *layout_data);
|
||||||
|
void on_open(http_manager::websocket_connection_ptr connection);
|
||||||
|
void on_message(http_manager::websocket_connection_ptr connection, const std::string &payload, int opcode);
|
||||||
|
void on_close(http_manager::websocket_connection_ptr connection, int status, const std::string& reason);
|
||||||
|
void on_error(http_manager::websocket_connection_ptr connection, const std::error_code& error_code);
|
||||||
|
void update_web_bcd(uint8_t digit_nbr, uint8_t m_segments);
|
||||||
|
|
||||||
|
http_manager::websocket_connection_ptr m_connection;
|
||||||
|
uint16_t m_web_line0;
|
||||||
|
uint16_t m_web_line1;
|
||||||
|
uint16_t m_web_line2;
|
||||||
|
uint16_t m_web_line3;
|
||||||
|
uint16_t m_web_line4;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
NETDEV_LOGIC_CALLBACK_MEMBER(prodigy_state::bcd_bit0_cb) { if (data != 0) m_digit |= 0x01; else m_digit &= ~(0x01); LOGBCD("%s: %d m_digit: %02x\n", FUNCNAME, data, m_digit); }
|
NETDEV_LOGIC_CALLBACK_MEMBER(prodigy_state::bcd_bit0_cb) { if (data != 0) m_digit |= 0x01; else m_digit &= ~(0x01); LOGBCD("%s: %d m_digit: %02x\n", FUNCNAME, data, m_digit); }
|
||||||
@ -167,8 +211,219 @@ NETDEV_LOGIC_CALLBACK_MEMBER(prodigy_state::bcd_bit7_cb) { if (data != 0) m_digi
|
|||||||
void prodigy_state::device_start()
|
void prodigy_state::device_start()
|
||||||
{
|
{
|
||||||
memset(m_line, 0, sizeof(m_line));
|
memset(m_line, 0, sizeof(m_line));
|
||||||
|
#if HTTPUI
|
||||||
|
m_server = machine().manager().http();
|
||||||
|
if (m_server->is_active())
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
m_server->add_http_handler("/layout*", std::bind(&prodigy_state::on_update, this, _1, _2));
|
||||||
|
m_server->add_endpoint("/socket",
|
||||||
|
std::bind(&prodigy_state::on_open, this, _1),
|
||||||
|
std::bind(&prodigy_state::on_message, this, _1, _2, _3),
|
||||||
|
std::bind(&prodigy_state::on_close, this, _1, _2, _3),
|
||||||
|
std::bind(&prodigy_state::on_error, this, _1, _2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HTTPUI
|
||||||
|
const std::string prodigy_state::decompress_layout_data(const internal_layout *layout_data)
|
||||||
|
{
|
||||||
|
// +1 to ensure data is terminated for XML parser
|
||||||
|
std::unique_ptr<u8> tempout(new u8(layout_data->decompressed_size + 1));
|
||||||
|
std::string fail("");
|
||||||
|
z_stream stream;
|
||||||
|
int zerr;
|
||||||
|
|
||||||
|
/* initialize the stream */
|
||||||
|
memset(&stream, 0, sizeof(stream));
|
||||||
|
stream.next_out = tempout.get();
|
||||||
|
stream.avail_out = layout_data->decompressed_size;
|
||||||
|
|
||||||
|
zerr = inflateInit(&stream);
|
||||||
|
if (zerr != Z_OK)
|
||||||
|
{
|
||||||
|
fatalerror("could not inflateInit");
|
||||||
|
return fail; // return empty buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decompress this chunk */
|
||||||
|
stream.next_in = (unsigned char*)layout_data->data;
|
||||||
|
stream.avail_in = layout_data->compressed_size;
|
||||||
|
zerr = inflate(&stream, Z_NO_FLUSH);
|
||||||
|
|
||||||
|
/* stop at the end of the stream */
|
||||||
|
if (zerr == Z_STREAM_END)
|
||||||
|
{
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
else if (zerr != Z_OK)
|
||||||
|
{
|
||||||
|
fatalerror("decompression error\n");
|
||||||
|
return fail; // return empty buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
zerr = inflateEnd(&stream);
|
||||||
|
if (zerr != Z_OK)
|
||||||
|
{
|
||||||
|
fatalerror("inflateEnd error\n");
|
||||||
|
return fail; // return empty buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string((const char *)tempout.get(), layout_data->decompressed_size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void prodigy_state::on_update(http_manager::http_request_ptr request, http_manager::http_response_ptr response)
|
||||||
|
{
|
||||||
|
LOG("%s\n", FUNCNAME);
|
||||||
|
|
||||||
|
LOGHTTP("Full request: %s\n", request->get_resource());
|
||||||
|
LOGHTTP("Path: %s\n", request->get_path());
|
||||||
|
LOGHTTP("Query: %s\n", request->get_query());
|
||||||
|
LOGHTTP("Artpath: %s\n", this->machine().options().art_path());
|
||||||
|
LOGHTTP("System: %s\n", machine().system().name);
|
||||||
|
|
||||||
|
response->set_status(200);
|
||||||
|
response->set_content_type("text/xml");
|
||||||
|
|
||||||
|
if (std::string("/layout/layout.xsl") == request->get_path())
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
osd_get_full_path(path, ".");
|
||||||
|
LOGHTTP("Serving XSL document %s\n", path + "/web/layout.xsl");
|
||||||
|
#if 0
|
||||||
|
m_server->serve_document(request, response, "layout.xls"); // Doesn't work, how do I make the webserver serve a file?
|
||||||
|
#else
|
||||||
|
std::ifstream xslfile (path + "/web/layout.xsl");
|
||||||
|
if (xslfile.is_open())
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
std::string outstr;
|
||||||
|
while ( std::getline (xslfile, buf) )
|
||||||
|
{
|
||||||
|
outstr += buf + "\n";
|
||||||
|
}
|
||||||
|
xslfile.close();
|
||||||
|
response->set_body(outstr);
|
||||||
|
LOGXML("%s", outstr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response->set_status(500); // Or 404...?
|
||||||
|
logerror("Unable to open file %s", path + "web/layout.xsl");
|
||||||
|
LOGHTTP("Unable to open file %s", path + "web/layout.xsl");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
z_stream stream;
|
||||||
|
int zerr;
|
||||||
|
char buf[1024 * 10];
|
||||||
|
std::string outstr;
|
||||||
|
memset(&stream, 0, sizeof(stream));
|
||||||
|
stream.next_in = (Bytef *)(layout_prodigy.data);
|
||||||
|
stream.avail_in = layout_prodigy.compressed_size;
|
||||||
|
if ((zerr = inflateInit(&stream)) == Z_OK)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
stream.next_out = (Bytef*)(&buf[0]);
|
||||||
|
stream.avail_out = sizeof(buf);
|
||||||
|
zerr = inflate(&stream, 0);
|
||||||
|
if (zerr == Z_OK || zerr == Z_STREAM_END)
|
||||||
|
{
|
||||||
|
outstr.append(&buf[0], stream.total_out - outstr.size());
|
||||||
|
LOGXML("appending %s\n", buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGHTTP("Append Error %d\n", zerr);
|
||||||
|
}
|
||||||
|
}while (zerr == Z_OK);
|
||||||
|
if ((zerr = inflateEnd(&stream)) != Z_OK)
|
||||||
|
{
|
||||||
|
response->set_status(500); // Or 404...?
|
||||||
|
fatalerror("Zlib decompression error\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outstr.insert(std::string("<?xml version=\"1.0\"?>").size(), "<?xml-stylesheet type=\"text/xml\" href=\"layout.xsl\"?>");
|
||||||
|
response->set_body(outstr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response->set_status(500); // Or 404...?
|
||||||
|
LOGHTTP("Init Error %d\n", zerr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void prodigy_state::on_open(http_manager::websocket_connection_ptr connection)
|
||||||
|
{
|
||||||
|
logerror("websocket connection opened\n");
|
||||||
|
LOGHTTP("%s\n", FUNCNAME);
|
||||||
|
m_connection = connection;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
update_web_bcd(i, m_digit_cache[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void prodigy_state::on_message(http_manager::websocket_connection_ptr connection, const std::string &payload, int opcode)
|
||||||
|
{
|
||||||
|
LOGHTTP("%s: %d - %s\n", FUNCNAME, opcode, payload);
|
||||||
|
using namespace rapidjson;
|
||||||
|
Document document;
|
||||||
|
document.Parse(payload.c_str());
|
||||||
|
LOGHTTP("%s - %04x: %s\n", document["inputtag"].GetString(), document["inputmask"].GetInt(), document["event"].GetInt() == 0 ? "mouse Down" : "mouse Up");
|
||||||
|
|
||||||
|
// TODO: Reduce number of LINE%u and match mouse up to last mouse down so that mouse clicks don't stick "forever" when gliding out of click down boundary
|
||||||
|
if (std::string(document["inputtag"].GetString()).compare("LINE0") == 0)
|
||||||
|
{
|
||||||
|
if (document["event"].GetInt() == 0) m_web_line0 |= document["inputmask"].GetInt(); else m_web_line0 &= ~(document["inputmask"].GetInt());
|
||||||
|
LOGAW("-WEBLINE0: %02x\n", m_web_line0);
|
||||||
|
}
|
||||||
|
else if (std::string(document["inputtag"].GetString()).compare("LINE1") == 0)
|
||||||
|
{
|
||||||
|
if (document["event"].GetInt() == 0) m_web_line1 |= document["inputmask"].GetInt(); else m_web_line1 &= ~(document["inputmask"].GetInt());
|
||||||
|
LOGAW("-WEBLINE1: %02x\n", m_web_line1);
|
||||||
|
}
|
||||||
|
else if (std::string(document["inputtag"].GetString()).compare("LINE2") == 0)
|
||||||
|
{
|
||||||
|
if (document["event"].GetInt() == 0) m_web_line2 |= document["inputmask"].GetInt(); else m_web_line2 &= ~(document["inputmask"].GetInt());
|
||||||
|
LOGAW("-WEBLINE2: %02x\n", m_web_line2);
|
||||||
|
}
|
||||||
|
else if (std::string(document["inputtag"].GetString()).compare("LINE3") == 0)
|
||||||
|
{
|
||||||
|
if (document["event"].GetInt() == 0) m_web_line3 |= document["inputmask"].GetInt(); else m_web_line3 &= ~(document["inputmask"].GetInt());
|
||||||
|
LOGAW("-WEBLINE3: %02x\n", m_web_line3);
|
||||||
|
}
|
||||||
|
else if (std::string(document["inputtag"].GetString()).compare("LINE4") == 0)
|
||||||
|
{
|
||||||
|
if (document["event"].GetInt() == 0) m_web_line4 |= document["inputmask"].GetInt(); else m_web_line4 &= ~(document["inputmask"].GetInt());
|
||||||
|
LOGAW("-WEBLINE4: %02x\n", m_web_line4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void prodigy_state::on_close(http_manager::websocket_connection_ptr connection, int status, const std::string& reason)
|
||||||
|
{
|
||||||
|
logerror("websocket connection closed: %d - %s\n", status, reason);
|
||||||
|
LOGHTTP("%s: %d - %s\n", FUNCNAME, status, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prodigy_state::on_error(http_manager::websocket_connection_ptr connection, const std::error_code& error_code)
|
||||||
|
{
|
||||||
|
logerror("websocket connection error: %d\n", error_code);
|
||||||
|
LOGHTTP("%s: %d\n", FUNCNAME, error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
WRITE_LINE_MEMBER(prodigy_state::via_cb1_w)
|
WRITE_LINE_MEMBER(prodigy_state::via_cb1_w)
|
||||||
{
|
{
|
||||||
LOGCLK("%s: %d\n", FUNCNAME, state);
|
LOGCLK("%s: %d\n", FUNCNAME, state);
|
||||||
@ -201,9 +456,13 @@ READ8_MEMBER( prodigy_state::via_pa_r )
|
|||||||
uint16_t ttl74145_data = m_74145->read();
|
uint16_t ttl74145_data = m_74145->read();
|
||||||
|
|
||||||
LOGKBD(" - 74145: %03x\n", ttl74145_data);
|
LOGKBD(" - 74145: %03x\n", ttl74145_data);
|
||||||
|
#if HTTPUI
|
||||||
|
if (ttl74145_data & 0x100) return ((m_line[0] & ~m_web_line0) | (m_line[1] & ~m_web_line1));
|
||||||
|
if (ttl74145_data & 0x200) return ((m_line[4] & ~m_web_line4) | (m_line[3] & ~m_web_line3));
|
||||||
|
#else
|
||||||
if (ttl74145_data & 0x100) return (m_line[0] | m_line[1]);
|
if (ttl74145_data & 0x100) return (m_line[0] | m_line[1]);
|
||||||
if (ttl74145_data & 0x200) return (m_line[4] | m_line[3]);
|
if (ttl74145_data & 0x200) return (m_line[4] | m_line[3]);
|
||||||
|
#endif
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,8 +470,14 @@ READ8_MEMBER( prodigy_state::via_pb_r )
|
|||||||
{
|
{
|
||||||
LOGKBD("%s: Port B <- %02x\n", FUNCNAME, 0);
|
LOGKBD("%s: Port B <- %02x\n", FUNCNAME, 0);
|
||||||
uint16_t ttl74145_data = m_74145->read();
|
uint16_t ttl74145_data = m_74145->read();
|
||||||
|
|
||||||
|
#if HTTPUI
|
||||||
|
if (ttl74145_data & 0x100) return ((((m_line[2] & ~m_web_line2) >> 8) & 3) << 4);
|
||||||
|
if (ttl74145_data & 0x200) return ((((m_line[2] & ~m_web_line2) >> 10) & 3) << 4);
|
||||||
|
#else
|
||||||
if (ttl74145_data & 0x100) return (((m_line[2] >> 8) & 3) << 4);
|
if (ttl74145_data & 0x100) return (((m_line[2] >> 8) & 3) << 4);
|
||||||
if (ttl74145_data & 0x200) return (((m_line[2] >> 10) & 3) << 4);
|
if (ttl74145_data & 0x200) return (((m_line[2] >> 10) & 3) << 4);
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
@ -241,6 +506,28 @@ WRITE8_MEMBER( prodigy_state::via_pb_w )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HTTPUI
|
||||||
|
void prodigy_state::update_web_bcd(uint8_t digit_nbr, uint8_t m_segments)
|
||||||
|
{
|
||||||
|
using namespace rapidjson;
|
||||||
|
Document d;
|
||||||
|
d.SetObject();
|
||||||
|
Document::AllocatorType& allocator = d.GetAllocator();
|
||||||
|
// Add data to JSON document
|
||||||
|
d.AddMember("name", "digit", allocator);
|
||||||
|
d.AddMember("number", digit_nbr, allocator);
|
||||||
|
d.AddMember("segments", m_segments, allocator);
|
||||||
|
// Convert JSON document to string
|
||||||
|
StringBuffer buf;
|
||||||
|
Writer<StringBuffer> writer(buf);
|
||||||
|
d.Accept(writer);
|
||||||
|
if (m_connection != NULL)
|
||||||
|
{
|
||||||
|
m_connection->send_message(buf.GetString(), 1);// 1 == Text 2 == binary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void prodigy_state::update_bcd()
|
void prodigy_state::update_bcd()
|
||||||
{
|
{
|
||||||
LOGBCD("%s\n", FUNCNAME);
|
LOGBCD("%s\n", FUNCNAME);
|
||||||
@ -265,7 +552,14 @@ void prodigy_state::update_bcd()
|
|||||||
LOGBCD(" - segments: %02x -> ", m_digit);
|
LOGBCD(" - segments: %02x -> ", m_digit);
|
||||||
m_segments = m_digit;
|
m_segments = m_digit;
|
||||||
LOGBCD("%02x\n", m_segments);
|
LOGBCD("%02x\n", m_segments);
|
||||||
output().set_digit_value( digit_nbr, m_segments);
|
if (m_digit_cache[digit_nbr] != m_segments)
|
||||||
|
{
|
||||||
|
output().set_digit_value( digit_nbr, m_segments);
|
||||||
|
m_digit_cache[digit_nbr] = m_segments;
|
||||||
|
#if HTTPUI
|
||||||
|
update_web_bcd(digit_nbr, m_segments);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<?xml-stylesheet type="text/xsl" href="layout.xsl"?>
|
<?xml-stylesheet type="text/xml" href="layout.xsl"?>
|
||||||
<mamelayout version="2">
|
<mamelayout version="2">
|
||||||
<element name="digit" defstate="1">
|
<element name="digit" defstate="1">
|
||||||
<led7seg>
|
<led7seg>
|
||||||
|
231
web/layout.xsl
Normal file
231
web/layout.xsl
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- license:BSD-3-Clause -->
|
||||||
|
<!-- copyright-holders:Joakim Larsson Edstrom -->
|
||||||
|
|
||||||
|
<!-- This XSLT converter has been tested on
|
||||||
|
Win-10: Chrome 61.0.3163.100 (Official Build) (64-bit), Firefox Version 56.0 (64-bitars), IE Edge 38.14393.1066.0.
|
||||||
|
Linux :
|
||||||
|
OSX :
|
||||||
|
Compatibility: Win10: IE Firefox Chrome Linux: OSX:
|
||||||
|
===================================================================================================================
|
||||||
|
HTML/SVG file generated by xsltproc OK OK OK
|
||||||
|
Transformation browing the .LAY file OK Nope Security
|
||||||
|
Transformation HTTP serving .LAY file OK Nope OK
|
||||||
|
===================================================================================================================
|
||||||
|
-->
|
||||||
|
|
||||||
|
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||||
|
<!-- From https://stackoverflow.com/questions/22905134/convert-a-hexadecimal-number-to-an-integer-in-xslt -->
|
||||||
|
<xsl:template name="decToHex">
|
||||||
|
<xsl:param name="dec"/>
|
||||||
|
<xsl:if test="$dec > 0">
|
||||||
|
<xsl:call-template name="decToHex">
|
||||||
|
<xsl:with-param name="dec" select="floor($dec div 16)"/>
|
||||||
|
</xsl:call-template>
|
||||||
|
<xsl:value-of select="substring('0123456789ABCDEF', (($dec mod 16) + 1), 1)"/>
|
||||||
|
</xsl:if>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
<!-- Handling of conversion of 0 as we know it should generate a two 0-digit sequence -->
|
||||||
|
<xsl:template name="decimalToHex">
|
||||||
|
<xsl:param name="dec"/>
|
||||||
|
<xsl:if test="$dec > 0">
|
||||||
|
<xsl:call-template name="decToHex">
|
||||||
|
<xsl:with-param name="dec" select="$dec"/>
|
||||||
|
</xsl:call-template>
|
||||||
|
</xsl:if>
|
||||||
|
<xsl:if test="$dec = 0">00</xsl:if>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
<!-- Generate a 6 hex digit representation from a .LAY <color> definition -->
|
||||||
|
<xsl:template match="color">
|
||||||
|
<xsl:param name="factor"/>
|
||||||
|
<xsl:call-template name="decimalToHex">
|
||||||
|
<xsl:with-param name="dec"><xsl:value-of select="floor(@red * 255 * $factor)"/></xsl:with-param>
|
||||||
|
</xsl:call-template>
|
||||||
|
<xsl:call-template name="decimalToHex">
|
||||||
|
<xsl:with-param name="dec"><xsl:value-of select="floor(@green * 255 * $factor)"/></xsl:with-param>
|
||||||
|
</xsl:call-template>
|
||||||
|
<xsl:call-template name="decimalToHex">
|
||||||
|
<xsl:with-param name="dec"><xsl:value-of select="floor(@blue * 255 * $factor)"/></xsl:with-param>
|
||||||
|
</xsl:call-template>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
<xsl:key name="getelement" match="mamelayout/element" use="@name"/>
|
||||||
|
|
||||||
|
<xsl:template match="/">
|
||||||
|
<xsl:variable name="this" select="."/>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Title</title>
|
||||||
|
<script language="javascript">
|
||||||
|
<![CDATA[
|
||||||
|
var defstate = {};
|
||||||
|
var ws;
|
||||||
|
function Message(tag, mask, event)
|
||||||
|
{
|
||||||
|
var msg = { };
|
||||||
|
msg.inputtag = tag;
|
||||||
|
msg.inputmask = mask;
|
||||||
|
msg.event = event;
|
||||||
|
ws.send(JSON.stringify(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
function LayoutConnection()
|
||||||
|
{
|
||||||
|
if ("WebSocket" in window)
|
||||||
|
{
|
||||||
|
ws = new WebSocket("ws://localhost:8080/socket");
|
||||||
|
|
||||||
|
ws.onopen = function() { console.log("Websocket open"); };
|
||||||
|
|
||||||
|
ws.onmessage = function (evt)
|
||||||
|
{
|
||||||
|
var msg = evt.data;
|
||||||
|
console.log("Websocket message: " + msg);
|
||||||
|
var dom = JSON.parse(msg);
|
||||||
|
if (dom.name == 'digit')
|
||||||
|
{
|
||||||
|
var digit = document.getElementById("digit" + dom.number).childNodes;
|
||||||
|
for (var index = 0; index < digit.length; index++)
|
||||||
|
{
|
||||||
|
if (dom.segments & (1 << index))
|
||||||
|
digit[index].setAttribute("fill", element_states["digit"][1]);
|
||||||
|
else
|
||||||
|
digit[index].setAttribute("fill", element_states["digit"][0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function() {console.log("Websocket connection closed"); alert("Websocket connection closed!"); };
|
||||||
|
|
||||||
|
window.onbeforeunload = function(event) { socket.close(); };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alert("Your Browser doesn't support websockets!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PageStart()
|
||||||
|
{
|
||||||
|
<!-- TODO: Loop through the default state array and set up all bezels using each element -->
|
||||||
|
console.log("Defstate: " + defstate);
|
||||||
|
for (var key in defstate) {
|
||||||
|
console.log("Bezel: " + key + "State:" + defstate[key]);
|
||||||
|
}
|
||||||
|
<!-- Setup the websocket connection -->
|
||||||
|
LayoutConnection();
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
<!-- Build default state array -->
|
||||||
|
<xsl:for-each select="mamelayout/element">
|
||||||
|
<xsl:if test="@defstate">
|
||||||
|
defstate['<xsl:value-of select="@name"/>'] = <xsl:value-of select="@defstate"/>;
|
||||||
|
</xsl:if>
|
||||||
|
</xsl:for-each>
|
||||||
|
|
||||||
|
var element_states = {
|
||||||
|
<!-- Build element state array -->
|
||||||
|
<xsl:for-each select="mamelayout/element">
|
||||||
|
<xsl:variable name="name"><xsl:value-of select="@name"/></xsl:variable>
|
||||||
|
<xsl:value-of select="$name"/>: {
|
||||||
|
<xsl:for-each select="*">
|
||||||
|
<xsl:variable name="elem"><xsl:value-of select="name()"/></xsl:variable>
|
||||||
|
<xsl:if test="@state"><xsl:value-of select="@state"/>: "#<xsl:apply-templates select="color"><xsl:with-param name="factor">1.0</xsl:with-param></xsl:apply-templates>",</xsl:if>
|
||||||
|
<xsl:if test="not(@state)">1: "#<xsl:apply-templates select="color"><xsl:with-param name="factor">1.0</xsl:with-param></xsl:apply-templates>",</xsl:if>
|
||||||
|
|
||||||
|
<!-- segment unlit handling. rendlay.cpp defines a 0x202020 mask for led segements that are turned off, eg an implicit state="0" -->
|
||||||
|
<xsl:if test="$elem = 'led7seg'">
|
||||||
|
<xsl:value-of select="@state"/>0: "#<xsl:apply-templates select="color"><xsl:with-param name="factor">0.13</xsl:with-param></xsl:apply-templates>",
|
||||||
|
</xsl:if>
|
||||||
|
|
||||||
|
</xsl:for-each> },
|
||||||
|
</xsl:for-each> };
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="PageStart();">
|
||||||
|
<xsl:for-each select="mamelayout/view">
|
||||||
|
<svg width="100%" height="100%" viewport="{bounds/@left} {bounds/@top} {bounds/@right} {bounds/@bottom}">
|
||||||
|
<xsl:for-each select="bezel">
|
||||||
|
<xsl:variable name="and"><![CDATA[&]]></xsl:variable>
|
||||||
|
<xsl:variable name="element"><xsl:value-of select="@element"/></xsl:variable>
|
||||||
|
<xsl:variable name="name"><xsl:value-of select="@name"/></xsl:variable>
|
||||||
|
<xsl:variable name="inputtag"><xsl:value-of select="@inputtag"/></xsl:variable>
|
||||||
|
<xsl:variable name="inputmask"><xsl:value-of select="@inputmask"/></xsl:variable>
|
||||||
|
<xsl:variable name="inputtag_onmousedown"><xsl:if test="@inputtag">Message('<xsl:value-of select="@inputtag"/>', <xsl:value-of select="@inputmask"/>, 0);</xsl:if></xsl:variable>
|
||||||
|
<xsl:variable name="inputtag_onmouseup"><xsl:if test="@inputtag">Message('<xsl:value-of select="@inputtag"/>', <xsl:value-of select="@inputmask"/>, 1);</xsl:if></xsl:variable>
|
||||||
|
<xsl:variable name="bounds_x"><xsl:value-of select="bounds/@x"/></xsl:variable>
|
||||||
|
<xsl:variable name="bounds_y"><xsl:value-of select="bounds/@y"/></xsl:variable>
|
||||||
|
<xsl:variable name="bounds_width"><xsl:value-of select="bounds/@width"/></xsl:variable>
|
||||||
|
<xsl:variable name="bounds_height"><xsl:value-of select="bounds/@height"/></xsl:variable>
|
||||||
|
<xsl:for-each select="key('getelement', $element)">
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test="rect">
|
||||||
|
<rect x="{$bounds_x}"
|
||||||
|
y="{$bounds_y}"
|
||||||
|
width="{$bounds_width}"
|
||||||
|
height="{$bounds_height}"
|
||||||
|
style="fill:rgb({floor(rect/color/@red * 255)},{floor(rect/color/@green * 255)},{floor(rect/color/@blue * 255)});stroke-width:0"
|
||||||
|
onmousedown="{$inputtag_onmousedown}"
|
||||||
|
onmouseup="{$inputtag_onmouseup}"
|
||||||
|
/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="text"><!-- TODO: Fix offsets, font and font size, .LAY seems to position from a centerpoint while SVG does it from left side. Needs investigation -->
|
||||||
|
<text x="{floor($bounds_x + $bounds_width * 0.25)}"
|
||||||
|
y="{floor($bounds_y + $bounds_height * 0.8)}"
|
||||||
|
font-family="sans-serif"
|
||||||
|
font-size="{floor($bounds_height * 0.75)}"
|
||||||
|
style="fill:rgb({floor(text/color/@red * 255)},{floor(text/color/@green * 255)},{floor(text/color/@blue * 255)});stroke-width:0.2;stroke:black"><xsl:value-of select="text/@string"/></text>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="disk"><!-- TODO: Fix state support -->
|
||||||
|
<xsl:variable name="defstate"><xsl:value-of select="@defstate"/></xsl:variable>
|
||||||
|
<xsl:for-each select="disk">
|
||||||
|
<!-- TODO: store state colors in an array for each disk element -->
|
||||||
|
</xsl:for-each>
|
||||||
|
<!-- TODO: use the color index $defstate -->
|
||||||
|
<ellipse cx="{floor($bounds_x + $bounds_width * 0.5)}"
|
||||||
|
cy="{floor($bounds_y + $bounds_height * 0.5)}"
|
||||||
|
rx="{floor($bounds_width * 0.5)}"
|
||||||
|
ry="{floor($bounds_height * 0.5)}"
|
||||||
|
style="fill:rgb({floor(disk/color/@red * 255)},{floor(disk/color/@green * 255)},{floor(disk/color/@blue * 255)});stroke-width:0.2;stroke:black"
|
||||||
|
onmousedown="{$inputtag_onmousedown}"
|
||||||
|
onmouseup="{$inputtag_onmouseup}"
|
||||||
|
/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="led7seg">
|
||||||
|
<g id="{$name}" style="fill-rule:evenodd; stroke:#000000; stroke-width:0.25; stroke-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;"
|
||||||
|
transform="translate(0,17) skewX(-4) translate(17,-17) translate({$bounds_x} {$bounds_y}) scale({$bounds_width div 11},{$bounds_height div 17}) ">
|
||||||
|
|
||||||
|
<!-- This definition is copied from Public Domain source: https://commons.wikimedia.org/wiki/File:7-segment_bcd.svg -->
|
||||||
|
<!-- TODO: Add decimal point -->
|
||||||
|
<polygon id="a" points=" 1, 1 2, 0 8, 0 9, 1 8, 2 2, 2" fill=""/>
|
||||||
|
<polygon id="b" points=" 9, 1 10, 2 10, 8 9, 9 8, 8 8, 2" fill=""/>
|
||||||
|
<polygon id="c" points=" 9, 9 10,10 10,16 9,17 8,16 8,10" fill=""/>
|
||||||
|
<polygon id="d" points=" 9,17 8,18 2,18 1,17 2,16 8,16" fill=""/>
|
||||||
|
<polygon id="e" points=" 1,17 0,16 0,10 1, 9 2,10 2,16" fill=""/>
|
||||||
|
<polygon id="f" points=" 1, 9 0, 8 0, 2 1, 1 2, 2 2, 8" fill=""/>
|
||||||
|
<polygon id="g" points=" 1, 9 2, 8 8, 8 9, 9 8,10 2,10" fill=""/>
|
||||||
|
</g>
|
||||||
|
<!-- debug -->
|
||||||
|
<!-- polygon points="{$bounds_x + 0}, {$bounds_y + 0}
|
||||||
|
{$bounds_x + 0}, {$bounds_y + $bounds_height}
|
||||||
|
{$bounds_x + $bounds_width}, {$bounds_y + $bounds_height}
|
||||||
|
{$bounds_x + $bounds_width}, {$bounds_y + 0}"
|
||||||
|
style="stroke-width:1;stroke:white;fill:none" / -->
|
||||||
|
<!-- /debug -->
|
||||||
|
</xsl:when>
|
||||||
|
</xsl:choose>
|
||||||
|
<xsl:text>
</xsl:text>
|
||||||
|
</xsl:for-each>
|
||||||
|
</xsl:for-each>
|
||||||
|
</svg>
|
||||||
|
</xsl:for-each>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
Loading…
Reference in New Issue
Block a user