diff options
author | 2013-08-12 13:56:55 +0000 | |
---|---|---|
committer | 2013-08-12 13:56:55 +0000 | |
commit | 5f184e36d63c2e9af0f96e7d98ad2fd14b0f3c68 (patch) | |
tree | 5c204c5c180776ee7ccbeacdae732ec82e9dc64b /src/emu/webengine.c | |
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/emu/webengine.c')
-rw-r--r-- | src/emu/webengine.c | 229 |
1 files changed, 229 insertions, 0 deletions
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)); + } +} |