Explorar el Código

initial commit

master
mortie hace 3 años
commit
b3d850e986
Se han modificado 21 ficheros con 1232 adiciones y 0 borrados
  1. 2
    0
      .gitignore
  2. 16
    0
      emuconf/desmume/config
  3. 33
    0
      emuconf/desmume/config.cfg
  4. 191
    0
      emuconf/vbam/vbam.conf
  5. 42
    0
      js/emulate.js
  6. 58
    0
      js/ipc.js
  7. 32
    0
      js/shell.js
  8. 27
    0
      package-lock.json
  9. 15
    0
      package.json
  10. 114
    0
      scripts/keymap.joystick.amgp
  11. 13
    0
      scripts/keymap.sh
  12. 81
    0
      server.js
  13. 10
    0
      udev/Makefile
  14. 83
    0
      udev/index.js
  15. 59
    0
      udev/src/json.c
  16. 16
    0
      udev/src/json.h
  17. 287
    0
      udev/src/main.c
  18. 17
    0
      web/index.html
  19. 62
    0
      web/ipc.js
  20. 35
    0
      web/script.js
  21. 39
    0
      web/style.css

+ 2
- 0
.gitignore Ver fichero

@@ -0,0 +1,2 @@
udev/udev-monitor
node_modules

+ 16
- 0
emuconf/desmume/config Ver fichero

@@ -0,0 +1,16 @@
[KEYS]
A=65293
B=65288
Select=101
Start=116
Right=65363
Left=65361
Up=65362
Down=65364
R=65366
L=65365
X=120
Y=121
Debug=49
Boost=50
Lid=51

+ 33
- 0
emuconf/desmume/config.cfg Ver fichero

@@ -0,0 +1,33 @@
[View]
ScreenLayout=2
SwapScreens=false
Rotation=0
ScreenGap=false
Filter=0
SecondaryFilter=3
ShowMenu=true
ShowToolbar=true
ShowStatusbar=true

[Window]
Scale2x=0
Fullscreen=true

[HudDisplay]
Fps=false
FrameCounter=false
LagCounter=false
Input=false
GraphicalInput=false
RTC=false
Mic=false

[Config]
FpsLimiter=true
AudoFrameskip=true
Frameskip=0

[Audio]
Enabled=true
Synchronization=0
Interpolation=1

+ 191
- 0
emuconf/vbam/vbam.conf Ver fichero

@@ -0,0 +1,191 @@
[Display]
Bilinear=1
Filter=none
FilterPlugin=
IFB=none
KeepOnTop=0
MaxThreads=4
RenderMethod=simple
Scale=3
Stretch=1
[GB]
BiosFile=
GBCBiosFile=
GBCROMDir=
PrintAutoPage=1
PrintScreenCap=0
ROMDir=
Palette0=7FFF,56B5,318C,0000,7FFF,56B5,318C,0000
Palette1=7FFF,56B5,318C,0000,7FFF,56B5,318C,0000
Palette2=7FFF,56B5,318C,0000,7FFF,56B5,318C,0000
[GBA]
BiosFile=
LinkAuto=0
LinkFast=0
LinkHost=
LinkProto=0
LinkTimeout=1
LinkType=0
ROMDir=
[General]
AutoLoadLastState=0
BatteryDir=
FreezeRecent=0
OnlineUpdates=daily
RecordingDir=
RewindInterval=0
ScreenshotDir=
StateDir=
StatusBar=0
[Joypad]
AutofireThrottle=1
Default=1
[Joypad/1]
Up=Up
Down=Down
Left=Left
Right=Right
A=X
B=Z
L=A
R=S
Select=
Start=Enter
MotionUp=
MotionDown=
MotionLeft=
MotionRight=
MotionIn=
MotionOut=
AutoA=W
AutoB=Q
Speed=Space
Capture=F11
GS=
[Joypad/2]
Up=
Down=
Left=
Right=
A=
B=
L=
R=
Select=
Start=
MotionUp=
MotionDown=
MotionLeft=
MotionRight=
MotionIn=
MotionOut=
AutoA=
AutoB=
Speed=
Capture=
GS=
[Joypad/3]
Up=
Down=
Left=
Right=
A=
B=
L=
R=
Select=
Start=
MotionUp=
MotionDown=
MotionLeft=
MotionRight=
MotionIn=
MotionOut=
AutoA=
AutoB=
Speed=
Capture=
GS=
[Joypad/4]
Up=
Down=
Left=
Right=
A=
B=
L=
R=
Select=
Start=
MotionUp=
MotionDown=
MotionLeft=
MotionRight=
MotionIn=
MotionOut=
AutoA=
AutoB=
Speed=
Capture=
GS=
[Sound]
AudioAPI=sdl
Buffers=5
Enable=783
GBAFiltering=50
GBAInterpolation=1
GBDeclicking=1
GBEcho=20
GBEnableEffects=0
GBStereo=15
GBSurround=0
Quality=44
Volume=100
[preferences]
LinkNumPlayers=2
agbPrint=0
autoFrameSkip=0
autoPatch=1
autoSaveCheatList=1
borderAutomatic=0
borderOn=0
captureFormat=0
cheatsEnabled=0
disableStatus=0
emulatorType=1
flashSize=0
frameSkip=0
fsColorDepth=32
fsFrequency=60
fsHeight=600
fsWidth=800
fullScreen=1
gbPaletteOption=0
gbPrinter=0
gdbBreakOnLoad=0
gdbPort=55555
maxScale=0
pauseWhenInactive=1
rtcEnabled=0
saveType=0
showSpeed=0
showSpeedTransparent=1
skipBios=0
skipSaveGameBattery=0
skipSaveGameCheats=0
throttle=100
useBiosGB=0
useBiosGBA=0
useBiosGBC=0
vsync=0
[Recent]
file1=roms/1986 - Pokemon Emerald (U)(TrashMan).gba
file2=/home/martin/Downloads/1986 - Pokemon Emerald (U)(TrashMan).gba
file3=
file4=
file5=
file6=
file7=
file8=
file9=
file10=

+ 42
- 0
js/emulate.js Ver fichero

@@ -0,0 +1,42 @@
var shell = require("./shell");
var pathlib = require("path");
var os = require("os");
var fs = require("fs");
var EventEmitter = require("events");

exports.run = run;

// Reset to default config
async function cpconf(from, to) {
to = pathlib.join(os.homedir(), to);
from = pathlib.join("emuconf", from);
await shell.safe("rm", "-rf", "--", to);
await shell.safe("cp", "-r", "--", from, to);
}

async function run_visualboy(path, saves) {
await cpconf("vbam", ".vbam");
await shell.safe("vbam", "-F", path);
}

async function run_desmume(path, saves) {
await cpconf("desmume", ".config/desmume");
await shell.safe("desmume", path);
}

var emus = {
nds: run_desmume,
gba: run_visualboy,
gbc: run_visualboy,
};

async function run(path, cb) {
var ext = pathlib.extname(path).substr(1);

var fn = emus[ext];
if (!fn)
throw new Error("Can't run games with extension "+ext+".");

shell.safe("mkdir", "-p", "--", path+".save");
await fn(path, path+".save");
}

+ 58
- 0
js/ipc.js Ver fichero

@@ -0,0 +1,58 @@
var WebSocket = require("ws");
var EventEmitter = require("events");

exports = module.exports = new EventEmitter();
exports.init = init;
exports.error = error;
exports.sendGameStopped = sendGameStopped;
exports.sendGameStart = sendGameStart;
exports.sendGameList = sendGameList;

var wss;

function onmessage(msg) {
console.log("Received", msg);
var obj = JSON.parse(msg);

switch (obj.cmd) {
case "run":
return exports.emit("run", obj.name);

default:
console.error("Received unknown command:", obj.cmd);
}
}

function send(obj) {
var msg = JSON.stringify(obj);
console.log("Sending", msg);
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN)
client.send(msg);
});
}

function init(port, cb) {
wss = new WebSocket.Server({ host: "127.0.0.1", port }, cb);

wss.on("connection", sock => {
exports.emit("connection");
sock.on("message", onmessage);
});
}

function sendGameStopped() {
send({ cmd: "ongamestopped" });
}

function sendGameStart() {
send({ cmd: "ongamestart" });
}

function sendGameList(games) {
send({ cmd: "ongamelist", games });
}

function error(msg) {
send({ cmd: "onerror", msg });
}

+ 32
- 0
js/shell.js Ver fichero

@@ -0,0 +1,32 @@
var spawn = require("child_process").spawn;

module.exports = shell;
module.exports.safe = safe;

function shell(cmd, ...args) {
console.log("running shell '"+cmd+" "+args.join(" ")+"'");
return new Promise((resolve, reject) => {
var child = spawn(cmd, args);

function print(out, name) {
return function(d) {
var s = d.toString();
if (s.trim() === "")
return;
out.write(cmd+": "+name+": "+s);
}
}
child.stderr.on("data", print(process.stderr, "err"));
child.stdout.on("data", print(process.stdout, "out"));

child.on("error", reject);
child.on("exit", resolve);
});
}

async function safe(...args) {
var code = await shell.apply(null, args);
if (code !== 0 && code !== null)
throw new Error("Command "+args[0]+" failed, exit code "+code);
return code;
}

+ 27
- 0
package-lock.json Ver fichero

@@ -0,0 +1,27 @@
{
"name": "emuboy",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"ultron": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz",
"integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ="
},
"ws": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.1.0.tgz",
"integrity": "sha512-TU4/qKFlyQFqNITNWiqPCUY9GqlAhEotlzfcZcve6VT1YEngQl1dDMqwQQS3eMYruJ5r/UD3lcsWib6iVMDGDw==",
"requires": {
"safe-buffer": "5.1.1",
"ultron": "1.1.0"
}
}
}
}

+ 15
- 0
package.json Ver fichero

@@ -0,0 +1,15 @@
{
"name": "emuboy",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"postinstall": "make -C udev"
},
"author": "Martin Dørum <martid0311@gmail.com> (http://mort.coffee)",
"license": "ISC",
"dependencies": {
"ws": "^3.1.0"
}
}

+ 114
- 0
scripts/keymap.joystick.amgp Ver fichero

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<joystick configversion="19" appversion="2.23">
<!--The SDL name for a joystick is included for informational purposes only.-->
<sdlname>usb gamepad </sdlname>
<!--The GUID for a joystick is included for informational purposes only.-->
<guid>030000001008000001e5000010010000</guid>
<sets>
<set index="1">
<axis index="1">
<throttle>normal</throttle>
<axisbutton index="1">
<slots>
<slot>
<code>0x1000012</code>
<mode>keyboard</mode>
</slot>
</slots>
</axisbutton>
<axisbutton index="2">
<slots>
<slot>
<code>0x1000014</code>
<mode>keyboard</mode>
</slot>
</slots>
</axisbutton>
</axis>
<axis index="2">
<maxZone>32737</maxZone>
<throttle>normal</throttle>
<axisbutton index="1">
<slots>
<slot>
<code>0x1000013</code>
<mode>keyboard</mode>
</slot>
</slots>
</axisbutton>
<axisbutton index="2">
<slots>
<slot>
<code>0x1000015</code>
<mode>keyboard</mode>
</slot>
</slots>
</axisbutton>
</axis>
<button index="1">
<slots>
<slot>
<code>0x58</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="2">
<slots>
<slot>
<code>0x1000004</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="3">
<slots>
<slot>
<code>0x1000003</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="4">
<slots>
<slot>
<code>0x59</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="5">
<slots>
<slot>
<code>0x1000016</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="6">
<slots>
<slot>
<code>0x1000017</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="9">
<slots>
<slot>
<code>0x1000001</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
<button index="10">
<slots>
<slot>
<code>0x1000000</code>
<mode>keyboard</mode>
</slot>
</slots>
</button>
</set>
</sets>
</joystick>

+ 13
- 0
scripts/keymap.sh Ver fichero

@@ -0,0 +1,13 @@
#!/bin/sh

# Left/Up/Down/Right: left/up/down/right
# A: Enter
# B: Backspace
# X: X
# Y: Y
# Start: T
# Select: E
# LBumper: PgUp
# RBumper: PgDn
dir=$(dirname "$0")
antimicro --hidden "$dir/keymap.joystick.amgp"

+ 81
- 0
server.js Ver fichero

@@ -0,0 +1,81 @@
var emu = require("./js/emulate");
var ipc = require("./js/ipc");
var shell = require("./js/shell");
var udev = require("./udev");
var pathlib = require("path");
var fs = require("fs");

var mountdir = "/tmp/emuboy.mnt"

process.on("unhandledRejection", evt => {
console.trace(evt);
process.exit(1);
});

var rompath = mountdir+"/roms";

var games = [];

function updateGames() {
if (driveMounted) {
var rx = /\.(gba|gbc|nds)$/;
fs.readdir(rompath, (err, list) => {
if (err)
throw err;

games = list.filter(x => rx.test(x));
ipc.sendGameList(games);
});
} else {
games = [];
ipc.sendGameList(games);
}
}

var driveMounted = false;
async function onblock(evt) {
if (evt.DEVTYPE !== "partition")
return;
if (evt.PARTN !== "1")
return;
if (evt.ID_BUS !== "usb")
return;

if (!evt.ACTION || evt.ACTION === "add") {
await shell.safe("mkdir", "-p", mountdir);
if (await shell("mountpoint", "-q", mountdir) === 0)
await shell.safe("sudo", "umount", mountdir);
await shell.safe(
"sudo", "mount", "-o", "uid="+process.env.USER,
evt.DEVNAME, mountdir);
await shell.safe("mkdir", "-p", rompath);
driveMounted = true;
updateGames();
} else if (evt.ACTION === "remove") {
driveMounted = false;
console.log("Unmounted drive", evt.DEVNAME);
updateGames();
}
}
udev.init();
udev.monitor("block", onblock);
udev.list("block", x => x.forEach(onblock));

ipc.init(8085, () => {
console.log("Server started");

ipc.on("run", async function(name) {
try {
ipc.sendGameStart();
await emu.run(pathlib.join(rompath, name));
} catch (err) {
ipc.error(err.toString());
console.trace(err);
}
ipc.sendGameStopped();
});

ipc.on("connection", () => {
updateGames();
});
});

+ 10
- 0
udev/Makefile Ver fichero

@@ -0,0 +1,10 @@
TARGET=udev-monitor

SRC=src/main.c src/json.c
HDR=src/json.h

$(TARGET): $(SRC) $(HDR)
$(CC) -o $(TARGET) -g $(shell pkg-config --cflags --libs libudev) $(SRC)

clean:
rm -f $(TARGET)

+ 83
- 0
udev/index.js Ver fichero

@@ -0,0 +1,83 @@
var spawn = require("child_process").spawn;

exports.list = list;
exports.monitor = monitor;
exports.unmonitor = unmonitor;
exports.exit = exit;
exports.init = init;

var child;

function init(cb) {
child = spawn(__dirname+"/udev-monitor");

var currstr = "";
child.stdout.setEncoding("utf8");
child.stdout.on("data", d => {
var lines = d.toString().split("\n");

currstr += lines[0];
if (lines.length === 1)
return;

ondata(JSON.parse(currstr));

for (var i = 1; i < lines.length -1; ++i) {
ondata(JSON.parse(lines[i]));
}

currstr = lines[lines.length - 1];
});
child.stderr.on("data", d => console.error("udev error:", d.toString()));
}

var listq = [];
var monitors = {};

function ondata(obj) {
if (obj instanceof Array) {
if (listq.length === 0)
return;
var cb = listq.shift();
cb(obj);
} else {
var ss = obj.SUBSYSTEM;
if (monitors[ss]) {
monitors[ss].forEach(cb => cb(obj));
}
if (monitors["*"]) {
monitors["*"].forEach(cb => cb(obj));
}
}
}

function list(ss, cb) {
listq.push(cb);
child.stdin.write("list:"+ss+"\n");
}

function monitor(ss, cb) {
if (monitors[ss] == null) {
child.stdin.write("monitor:"+ss+"\n");
monitors[ss] = [];
}

monitors[ss].push(cb);
}

function unmonitor(ss, cb) {
if (monitors[ss] == null)
throw new Error("Callback function for "+ss+" not registered");

var removed = false;
for (var i in monitors[ss]) {
if (monitors[ss][i] === cb) {
monitors[ss].splice(i, 1);
removed = true;
}
}
}

function exit() {
child.kill("SIGTERM");
}

+ 59
- 0
udev/src/json.c Ver fichero

@@ -0,0 +1,59 @@
#include "json.h"
#include <stdio.h>

void json_str(FILE *f, const char *str)
{
int start, i;
char c;

fprintf(f, "\"");

start = 0;
i = 0;
while (1)
{
c = str[i];
if (c == '\0')
{
fwrite(str + start, 1, i - start, f);
break;
}
else if (c == '\\' || c == '\"')
{
fwrite(str + start, 1, i - start, f);
fprintf(f, "\\%c", str[i]);
start = i + 1;
}

i += 1;
}

fprintf(f, "\"");
}
void json_sep(FILE *f)
{
fprintf(f, ",");
}

void json_obj_start(FILE *f)
{
fprintf(f, "{");
}
void json_obj_key(FILE *f, const char *key)
{
json_str(f, key);
fprintf(f, ":");
}
void json_obj_end(FILE *f)
{
fprintf(f, "}");
}

void json_arr_start(FILE *f)
{
fprintf(f, "[");
}
void json_arr_end(FILE *f)
{
fprintf(f, "]");
}

+ 16
- 0
udev/src/json.h Ver fichero

@@ -0,0 +1,16 @@
#ifndef JSON_H
#define JSON_H

#include <stdio.h>

void json_str(FILE *f, const char *str);
void json_sep(FILE *f);

void json_obj_start(FILE *f);
void json_obj_key(FILE *f, const char *key);
void json_obj_end(FILE *f);

void json_arr_start(FILE *f);
void json_arr_end(FILE *f);

#endif

+ 287
- 0
udev/src/main.c Ver fichero

@@ -0,0 +1,287 @@
#include "json.h"
#include <libudev.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

struct udev *udev;

FILE *out;

struct listener
{
struct udev_monitor *mon;
int fd;
};

struct listener listeners[32];
int listenerc = 0;

struct udev_monitor *mons[32];

static void devinfo_print_json(
FILE *f,
struct udev *udev,
struct udev_device *dev)
{
struct udev_list_entry *entry, *sysattrs;
int first;
const char *key, *val;

json_obj_start(f);

sysattrs = udev_device_get_properties_list_entry(dev);
first = 1;
udev_list_entry_foreach(entry, sysattrs)
{
key = udev_list_entry_get_name(entry);
val = udev_list_entry_get_value(entry);

if (!first)
json_sep(f);
json_obj_key(f, key);
json_str(f, val);

first = 0;
}

json_obj_end(f);

udev_device_unref(dev);
}

static void enumerate(FILE *f, const char *subsystem)
{
struct udev_enumerate *en;
struct udev_list_entry *device, *devices;
struct udev_device *dev;
int first;
const char *name;

// Create enumerator
en = udev_enumerate_new(udev);
if (subsystem != NULL)
udev_enumerate_add_match_subsystem(en, subsystem);

json_arr_start(f);

// Enumerate
udev_enumerate_scan_devices(en);
devices = udev_enumerate_get_list_entry(en);
first = 1;
udev_list_entry_foreach(device, devices)
{
if (!first)
json_sep(f);

name = udev_list_entry_get_name(device);
dev = udev_device_new_from_syspath(udev, name);
devinfo_print_json(f, udev, dev);

first = 0;
}

json_arr_end(f);

printf("\n");
fflush(f);

udev_enumerate_unref(en);
}

int monitor(const char *subsystem)
{
struct udev_monitor *mon;
struct listener *l;

mon = udev_monitor_new_from_netlink(udev, "udev");
if (!mon)
return -1;

if (subsystem != NULL)
{
udev_monitor_filter_add_match_subsystem_devtype(
mon, subsystem, NULL);
}
udev_monitor_enable_receiving(mon);

l = &listeners[listenerc++];
l->mon = mon;
l->fd = udev_monitor_get_fd(mon);

return 0;
}

void oncommand(struct listener *l)
{
char buf[1024];
char *cmd, *arg;
int cnt, i;
char c;

memset(buf, 0, sizeof(buf));

cnt = read(l->fd, buf, sizeof(buf));
if (cnt == -1)
{
perror("stdin");
return;
}
else if (cnt == 0)
{
// don't try to read from stdin anymore if it's closed
l->fd = -1;
return;
}
else if (cnt == sizeof(buf))
{
fprintf(stderr, "input buffer too big\n");

// drain, so we don't end up reading stale data next time
do {
cnt = read(l->fd, buf, sizeof(buf));
if (cnt == -1)
{
perror("stdin");
return;
}
} while (cnt == sizeof(buf));
return;
}

// parse input into command and argument
cmd = buf;
arg = NULL;
i = 0;
while (1)
{
c = buf[i];

// colon: replace with \0 to terminate cmd,
// update arg to point to the charcater after :
if (c == ':')
{
buf[i] = '\0';
arg = buf + i + 1;
}

// newline: replace with \0 to terminate arg,
// respond to request, then update cmd and
// continue reading the buffer until we reach a \0
else if (c == '\n' || c == '\0')
{
buf[i] = '\0';

if (*cmd == '\0')
break;

// respond to request
if (strcmp(cmd, "list") == 0)
{
if (strcmp(arg, "*") == 0)
enumerate(out, NULL);
else
enumerate(out, arg);
}
else if (strcmp(cmd, "monitor") == 0)
{
if (strcmp(arg, "*") == 0)
monitor(NULL);
else
monitor(arg);
}
else
{
fprintf(stderr, "Unknown command: %s\n", cmd);
}

if (c == '\0')
break;

cmd = buf + i + 1;
}

i += 1;
}
}

void onevent(struct listener *l)
{
struct udev_device *dev = udev_monitor_receive_device(l->mon);
if (!dev)
{
fprintf(stderr, "No device even though an event occurred.\n");
return;
}

devinfo_print_json(out, udev, dev);
printf("\n");
fflush(out);
}

void readinput()
{
fd_set set;
FD_ZERO(&set);
int max = -1;
for (int i = 0; i < listenerc; ++i)
{
int fd = listeners[i].fd;
if (fd == -1)
continue;
FD_SET(fd, &set);
if (fd > max)
max = fd;
}

int activity;

activity = select(max + 1, &set, NULL, NULL, NULL);
if (activity < 0)
{
if (errno != EINTR)
perror("select");
return;
}

for (int i = 0; i < listenerc; ++i)
{
struct listener *l = &listeners[i];
int fd = l->fd;
if (!FD_ISSET(fd, &set))
continue;

if (fd == STDIN_FILENO)
oncommand(l);
else
onevent(l);
}
}

int main()
{
out = stdout;
memset(listeners, 0, sizeof(listeners));

// read stdin
listeners[listenerc++].fd = STDIN_FILENO;

// create udev
udev = udev_new();
if (!udev)
{
fprintf(stderr, "Failed to craete udev\n");
return 1;
}

// main loop
while (1) {
readinput();
}

udev_unref(udev);
}

+ 17
- 0
web/index.html Ver fichero

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<title></title>
</head>
<body>
<div id="games"></div>
<div id="overlay">
Game running.
</div>

<script src="ipc.js"></script>
<script src="script.js"></script>
</body>
</html>

+ 62
- 0
web/ipc.js Ver fichero

@@ -0,0 +1,62 @@
(function() {
window.ipc = {};
ipc.ongamestopped = null;
ipc.ongamelist = null;
ipc.onerror = null;

var ws;
var connected = false;
function send(obj) {
var msg = JSON.stringify(obj);

if (!connected)
return console.error(
"Attempt to send message, but socket is closed.");

console.log("Sending", msg);
ws.send(msg);
}

ipc.init = (port, cb) => {
console.log("Connecting...");
ws = new WebSocket("ws://127.0.0.1:"+port);

ws.onopen = () => {
connected = true;
console.log("Connected.");
cb && cb();
}

ws.onmessage = msg => {
console.log("Received", msg.data);
var obj = JSON.parse(msg.data);

switch (obj.cmd) {
case "ongamestopped":
return ipc.ongamestopped();

case "ongamestart":
return ipc.ongamestart();

case "ongamelist":
return ipc.ongamelist(obj.games);

case "onerror":
return ipc.onerror(obj.msg);

default:
console.error("Received unknown command:", obj.cmd);
}
}

ws.onclose = () => {
connected = false;
console.log("Disconnected, retrying in 1 second...");
setTimeout(ipc.init.bind(null, port, cb), 1000);
}
}

ipc.run = name => {
send({ cmd: "run", name: name });
}
})();

+ 35
- 0
web/script.js Ver fichero

@@ -0,0 +1,35 @@
var elems = {
games: document.getElementById("games"),
overlay: document.getElementById("overlay"),
};

function makeGame(game) {
var a = document.createElement("a");
a.innerText = game;
a.href = "javascript:void(0)";
a.onclick = ipc.run.bind(null, game);
return a;
}

ipc.init(8085, () => {
elems.overlay.className = "";
});

ipc.ongamestopped = () => {
console.log("Game stopped.");
elems.overlay.className = "";
}

ipc.ongamestart = () => {
console.log("Game started.");
elems.overlay.className = "active";
}

ipc.ongamelist = games => {
elems.games.innerHTML = "";
games.forEach(g => elems.games.appendChild(makeGame(g)));
}

ipc.onerror = msg => {
alert(msg);
}

+ 39
- 0
web/style.css Ver fichero

@@ -0,0 +1,39 @@
html, body {
overflow: hidden;
position: fixed;
width: 100%;
height: 100%;
margin: 0px;
}

#games {
padding: 24px;
}
#games a {
margin: 12px;
display: block;
}

#overlay {
transition: opacity 0.5s, transform 0.5s;
opacity: 0;
pointer-events: none;

position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;

background-color: black;
color: white;
transform: scale(2);

display: flex;
align-items: center;
justify-content: center;
}
#overlay.active {
opacity: 1;
transform: scale(1);
}

Cargando…
Cancelar
Guardar