summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitattributes2
-rw-r--r--src/emu/emu.mak1
-rw-r--r--src/emu/mame.c59
-rw-r--r--src/emu/webengine.c229
-rw-r--r--src/emu/webengine.h74
-rw-r--r--src/lib/web/mongoose.c3
-rw-r--r--web/index.html26
7 files changed, 341 insertions, 53 deletions
diff --git a/.gitattributes b/.gitattributes
index 647a0547c7b..90bca1728e6 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1942,6 +1942,8 @@ src/emu/video/video.mak svneol=native#text/plain
src/emu/video/vooddefs.h svneol=native#text/plain
src/emu/video/voodoo.c svneol=native#text/plain
src/emu/video/voodoo.h svneol=native#text/plain
+src/emu/webengine.c svneol=native#text/plain
+src/emu/webengine.h svneol=native#text/plain
src/ldplayer/layout/pr8210.lay svneol=native#text/plain
src/ldplayer/ldplayer.c svneol=native#text/plain
src/ldplayer/ldplayer.lst svneol=native#text/plain
diff --git a/src/emu/emu.mak b/src/emu/emu.mak
index b24a06d0c8c..0c8fc651f12 100644
--- a/src/emu/emu.mak
+++ b/src/emu/emu.mak
@@ -131,6 +131,7 @@ EMUOBJS = \
$(EMUOBJ)/debug/textbuf.o \
$(EMUOBJ)/debugint/debugint.o \
$(EMUOBJ)/profiler.o \
+ $(EMUOBJ)/webengine.o \
$(OSDOBJ)/osdepend.o \
$(OSDOBJ)/osdnet.o
diff --git a/src/emu/mame.c b/src/emu/mame.c
index c2853249766..e7ada704667 100644
--- a/src/emu/mame.c
+++ b/src/emu/mame.c
@@ -84,8 +84,7 @@
#include "crsshair.h"
#include "validity.h"
#include "debug/debugcon.h"
-#include "web/mongoose.h"
-#include "web/json/json.h"
+#include "webengine.h"
#include <time.h>
@@ -132,33 +131,6 @@ int mame_is_valid_machine(running_machine &machine)
return (&machine == global_machine);
}
-// This function will be called by mongoose on every new request.
-static int begin_request_handler(struct mg_connection *conn) {
- const struct mg_request_info *request_info = mg_get_request_info(conn);
- if (!strcmp(request_info->uri, "/hello")) {
- Json::Value data;
- data["key1"] = "data1";
- data["key2"] = "data2";
- data["key3"] = "data3";
- data["key4"] = "data4";
-
- Json::FastWriter writer;
- const char *json = writer.write(data).c_str();
- // Send HTTP reply to the client
- mg_printf(conn,
- "HTTP/1.1 200 OK\r\n"
- "Content-Type: application/json\r\n"
- "Content-Length: %d\r\n" // Always set Content-Length
- "\r\n"
- "%s",
- (int)strlen(json), json);
-
- // Returning non-zero tells mongoose that our function has replied to
- // the client, and mongoose should not send client any more data.
- return 1;
- }
- return 0;
-}
/*-------------------------------------------------
mame_execute - run the core emulation
-------------------------------------------------*/
@@ -172,27 +144,12 @@ int mame_execute(emu_options &options, osd_interface &osd)
if (options.verbose())
print_verbose = true;
- struct mg_context *ctx = NULL;
- struct mg_callbacks callbacks;
-
- // List of options. Last element must be NULL.
- const char *web_options[] = {
- "listening_ports", options.http_port(),
- "document_root", options.http_path(),
- NULL
- };
-
- // Prepare callbacks structure.
- memset(&callbacks, 0, sizeof(callbacks));
- callbacks.begin_request = begin_request_handler;
-
- // Start the web server.
- if (options.http())
- ctx = mg_start(&callbacks, NULL, web_options);
-
// loop across multiple hard resets
bool exit_pending = false;
int error = MAMERR_NONE;
+
+ web_engine web(options);
+
while (error == MAMERR_NONE && !exit_pending)
{
// if no driver, use the internal empty driver
@@ -230,7 +187,9 @@ int mame_execute(emu_options &options, osd_interface &osd)
// looooong term: remove this
global_machine = &machine;
-
+
+ web.set_machine(machine);
+ web.push_message("update_machine");
// run the machine
error = machine.run(firstrun);
firstrun = false;
@@ -247,10 +206,6 @@ int mame_execute(emu_options &options, osd_interface &osd)
// machine will go away when we exit scope
global_machine = NULL;
}
-
- // Stop the server.
- if (options.http())
- mg_stop(ctx);
// return an error
return error;
}
diff --git a/src/emu/webengine.c b/src/emu/webengine.c
new file mode 100644
index 00000000000..17375b2beba
--- /dev/null
+++ b/src/emu/webengine.c
@@ -0,0 +1,229 @@
+/***************************************************************************
+
+ webengine.c
+
+ Handle MAME internal web server.
+
+****************************************************************************
+
+ Copyright Miodrag Milanovic
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY MIODRAG MILANOVIC ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL MIODRAG MILANOVIC BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "webengine.h"
+#include "web/mongoose.h"
+#include "web/json/json.h"
+
+//**************************************************************************
+// WEB ENGINE
+//**************************************************************************
+
+void web_engine::websocket_ready_handler(struct mg_connection *conn) {
+ static const char *message = "update_machine";
+ mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
+ m_websockets.append(*global_alloc(simple_list_wrapper<mg_connection>(conn)));
+}
+
+// Arguments:
+// flags: first byte of websocket frame, see websocket RFC,
+// http://tools.ietf.org/html/rfc6455, section 5.2
+// data, data_len: payload data. Mask, if any, is already applied.
+int web_engine::websocket_data_handler(struct mg_connection *conn, int flags,
+ char *data, size_t data_len)
+{
+ // just Echo example for now
+ if ((flags & 0x0f) == WEBSOCKET_OPCODE_TEXT)
+ mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
+
+ // Returning zero means stoping websocket conversation.
+ // Close the conversation if client has sent us "exit" string.
+ return memcmp(data, "exit", 4);
+}
+
+// This function will be called by mongoose on every new request.
+int web_engine::begin_request_handler(struct mg_connection *conn)
+{
+ const struct mg_request_info *request_info = mg_get_request_info(conn);
+ if (!strncmp(request_info->uri, "/json/",6))
+ {
+ if (!strcmp(request_info->uri, "/json/game"))
+ {
+ Json::Value data;
+ data["name"] = m_machine->system().name;
+ data["description"] = m_machine->system().description;
+ data["year"] = m_machine->system().year;
+ data["manufacturer"] = m_machine->system().manufacturer;
+ data["parent"] = m_machine->system().parent;
+ data["source_file"] = m_machine->system().source_file;
+ data["flags"] = m_machine->system().flags;
+
+ Json::FastWriter writer;
+ const char *json = writer.write(data).c_str();
+ // Send HTTP reply to the client
+ mg_printf(conn,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: application/json\r\n"
+ "Content-Length: %d\r\n" // Always set Content-Length
+ "\r\n"
+ "%s",
+ (int)strlen(json), json);
+
+ // Returning non-zero tells mongoose that our function has replied to
+ // the client, and mongoose should not send client any more data.
+ mg_close_connection(conn);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+void *web_engine::websocket_keepalive()
+{
+ while(!m_exiting_core)
+ {
+ osd_ticks_t curtime = osd_ticks();
+ if ((curtime - m_lastupdatetime) > osd_ticks_per_second() * 5)
+ {
+ m_lastupdatetime = curtime;
+ for (simple_list_wrapper<mg_connection> *curitem = m_websockets.first(); curitem != NULL; curitem = curitem->next())
+ {
+ mg_websocket_write(curitem->object(), WEBSOCKET_OPCODE_PING, NULL, 0);
+ }
+ }
+ osd_sleep(osd_ticks_per_second()/5);
+ }
+ return NULL;
+}
+
+//-------------------------------------------------
+// static callbacks
+//-------------------------------------------------
+static void websocket_ready_handler_static(struct mg_connection *conn)
+{
+ const struct mg_request_info *request_info = mg_get_request_info(conn);
+ web_engine *engine = downcast<web_engine *>(request_info->user_data);
+ engine->websocket_ready_handler(conn);
+}
+
+static int websocket_data_handler_static(struct mg_connection *conn, int flags,
+ char *data, size_t data_len)
+{
+ const struct mg_request_info *request_info = mg_get_request_info(conn);
+ web_engine *engine = downcast<web_engine *>(request_info->user_data);
+ return engine->websocket_data_handler(conn, flags, data, data_len);
+}
+
+static int begin_request_handler_static(struct mg_connection *conn)
+{
+ const struct mg_request_info *request_info = mg_get_request_info(conn);
+ web_engine *engine = downcast<web_engine *>(request_info->user_data);
+ return engine->begin_request_handler(conn);
+}
+
+static void *websocket_keepalive_static(void *thread_func_param)
+{
+ web_engine *engine = downcast<web_engine *>(thread_func_param);
+ return engine->websocket_keepalive();
+}
+
+//-------------------------------------------------
+// web_engine - constructor
+//-------------------------------------------------
+
+web_engine::web_engine(emu_options &options)
+ : m_options(options),
+ m_machine(NULL),
+ m_ctx(NULL),
+ m_lastupdatetime(0),
+ m_exiting_core(false)
+
+{
+
+ struct mg_callbacks callbacks;
+
+ // List of options. Last element must be NULL.
+ const char *web_options[] = {
+ "listening_ports", options.http_port(),
+ "document_root", options.http_path(),
+ NULL
+ };
+
+ // Prepare callbacks structure.
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.begin_request = begin_request_handler_static;
+ callbacks.websocket_ready = websocket_ready_handler_static;
+ callbacks.websocket_data = websocket_data_handler_static;
+
+ // Start the web server.
+ if (m_options.http()) {
+ m_ctx = mg_start(&callbacks, this, web_options);
+
+ mg_start_thread(websocket_keepalive_static, this);
+ }
+
+}
+
+//-------------------------------------------------
+// ~web_engine - destructor
+//-------------------------------------------------
+
+web_engine::~web_engine()
+{
+ if (m_options.http())
+ close();
+}
+
+//-------------------------------------------------
+// close - close and cleanup of lua engine
+//-------------------------------------------------
+
+void web_engine::close()
+{
+ m_exiting_core = 1;
+ osd_sleep(osd_ticks_per_second()/5);
+ for (simple_list_wrapper<mg_connection> *curitem = m_websockets.first(); curitem != NULL; curitem = curitem->next())
+ {
+ mg_websocket_write(curitem->object(), WEBSOCKET_OPCODE_CONNECTION_CLOSE, NULL, 0);
+ }
+ // Stop the server.
+ mg_stop(m_ctx);
+}
+
+
+void web_engine::push_message(const char *message)
+{
+ for (simple_list_wrapper<mg_connection> *curitem = m_websockets.first(); curitem != NULL; curitem = curitem->next())
+ {
+ mg_websocket_write(curitem->object(), WEBSOCKET_OPCODE_TEXT, message, strlen(message));
+ }
+}
diff --git a/src/emu/webengine.h b/src/emu/webengine.h
new file mode 100644
index 00000000000..9ab53aa65a6
--- /dev/null
+++ b/src/emu/webengine.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+
+ webengine.h
+
+ Handle MAME internal web server.
+
+****************************************************************************
+
+ Copyright Miodrag Milanovic
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY MIODRAG MILANOVIC ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL MIODRAG MILANOVIC BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __WEB_ENGINE_H__
+#define __WEB_ENGINE_H__
+
+struct mg_context; // Handle for the HTTP service itself
+struct mg_connection; // Handle for the individual connection
+
+class web_engine
+{
+public:
+ // construction/destruction
+ web_engine(emu_options &options);
+ ~web_engine();
+
+ void push_message(const char *message);
+ void close();
+
+ void set_machine(running_machine &machine) { m_machine = &machine; }
+
+ void websocket_ready_handler(struct mg_connection *conn);
+ int websocket_data_handler(struct mg_connection *conn, int flags, char *data, size_t data_len);
+ int begin_request_handler(struct mg_connection *conn);
+ void *websocket_keepalive();
+private:
+ // internal state
+ emu_options & m_options;
+ running_machine * m_machine;
+ struct mg_context * m_ctx;
+ osd_ticks_t m_lastupdatetime;
+ bool m_exiting_core;
+ simple_list<simple_list_wrapper<mg_connection> > m_websockets;
+};
+
+#endif /* __web_engine_H__ */
diff --git a/src/lib/web/mongoose.c b/src/lib/web/mongoose.c
index aad9c735bc5..7f5dee1a48c 100644
--- a/src/lib/web/mongoose.c
+++ b/src/lib/web/mongoose.c
@@ -19,6 +19,7 @@
// THE SOFTWARE.
#define NO_SSL
+#define USE_WEBSOCKET
#if defined(_WIN32)
#define SETSOCKOPT_CAST const char *
@@ -4052,7 +4053,7 @@ static void read_websocket(struct mg_connection *conn) {
if (header_len > 0) {
// Allocate space to hold websocket payload
data = mem;
- if (data_len > sizeof(mem) && (data = malloc(data_len)) == NULL) {
+ if (data_len > sizeof(mem) && (data = (char*)malloc(data_len)) == NULL) {
// Allocation failed, exit the loop and then close the connection
// TODO: notify user about the failure
break;
diff --git a/web/index.html b/web/index.html
index 8dbcce5dd35..90d013a646d 100644
--- a/web/index.html
+++ b/web/index.html
@@ -11,6 +11,31 @@
<link href="css/default.ultimate.css" media="screen" rel="stylesheet" type="text/css" />
</head>
<body>
+<script language="javascript" type="text/javascript">
+
+ var writeToScreen = function(message) {
+ var div = document.createElement('div');
+ div.innerHTML = message;
+ document.getElementById('output').appendChild(div);
+ };
+ window.onload = function() {
+ var url = 'ws://localhost:8080/foo';
+ websocket = new WebSocket(url);
+ websocket.onopen = function(ev) {
+ writeToScreen('<span style="color: green;">CONNECTED</span>');
+ };
+ websocket.onclose = function(ev) {
+ writeToScreen('<span style="color: white;">DISCONNECTED</span>');
+ };
+ websocket.onmessage = function(ev) {
+ writeToScreen('<span style="color: blue;">RESPONSE: ' + ev.data +
+ ' </span>');
+ };
+ websocket.onerror = function(ev) {
+ writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
+ };
+ };
+</script>
<div data-role="page">
<div data-role="header">
<img src="images/logo-mame-small.png"/>
@@ -46,6 +71,7 @@
</li>
<li id="n-debugger"><a href="./">Debugger</a></li>
</ul>
+<br/><br/><div id="output"></div>
</div>
</body>
</html> \ No newline at end of file