mirror of
https://github.com/holub/mame
synced 2025-07-02 00:29:37 +03:00
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)
This commit is contained in:
parent
59837a1e7d
commit
5f184e36d6
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
229
src/emu/webengine.c
Normal file
229
src/emu/webengine.c
Normal file
@ -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));
|
||||
}
|
||||
}
|
74
src/emu/webengine.h
Normal file
74
src/emu/webengine.h
Normal file
@ -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__ */
|
@ -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;
|
||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user