diff options
author | 2013-08-12 13:56:55 +0000 | |
---|---|---|
committer | 2013-08-12 13:56:55 +0000 | |
commit | 5f184e36d63c2e9af0f96e7d98ad2fd14b0f3c68 (patch) | |
tree | 5c204c5c180776ee7ccbeacdae732ec82e9dc64b /src | |
parent | 59837a1e7df63b744d6168faeb3a78520b4b6cdd (diff) |
Moved webengine into separate file, added callbacks for websockets and made example for triggering ui on driver change, also added json for game driver (nw)
Diffstat (limited to 'src')
-rw-r--r-- | src/emu/emu.mak | 1 | ||||
-rw-r--r-- | src/emu/mame.c | 59 | ||||
-rw-r--r-- | src/emu/webengine.c | 229 | ||||
-rw-r--r-- | src/emu/webengine.h | 74 | ||||
-rw-r--r-- | src/lib/web/mongoose.c | 3 |
5 files changed, 313 insertions, 53 deletions
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; |