diff --git a/src/GameManager.js b/src/GameManager.js
index 08e0b7b..5ec2caf 100644
--- a/src/GameManager.js
+++ b/src/GameManager.js
@@ -7,7 +7,9 @@ class EJS_GameManager {
getStateInfo: this.Module.cwrap('get_state_info', 'string', []), //these names are dumb
saveStateInfo: this.Module.cwrap('save_state_info', 'null', []),
loadState: this.Module.cwrap('load_state', 'number', ['string', 'number']),
- screenshot: this.Module.cwrap('cmd_take_screenshot', '', [])
+ screenshot: this.Module.cwrap('cmd_take_screenshot', '', []),
+ simulateInput: this.Module.cwrap('simulate_input', 'null', ['number', 'number', 'number']),
+ toggleMainLoop: this.Module.cwrap('toggleMainLoop', 'null', ['number'])
}
}
restart() {
@@ -71,6 +73,12 @@ class EJS_GameManager {
this.functions.loadState(name, 0);
})();
}
+ simulateInput(player, index, value) {
+ this.functions.simulateInput(player, index, value);
+ }
+ toggleMainLoop(playing) {
+ this.functions.toggleMainLoop(playing);
+ }
}
diff --git a/src/css/main.css b/src/css/main.css
index e87e0a6..fd05747 100644
--- a/src/css/main.css
+++ b/src/css/main.css
@@ -275,7 +275,6 @@
}
.ejs_control_body input[type='text'] {
- overflow: auto;
background-color: #fff;
border: 1px solid #000;
font-size: 12px;
@@ -307,3 +306,18 @@
.ejs_control_selected a {
color: #000 !important;
}
+
+.ejs_control_bar:hover {
+ background-color: #2d2d2d;
+}
+
+.ejs_popup_box {
+ position: absolute;
+ width: 300px;
+ top: 50%;
+ margin-left: -150px;
+ margin-top: -50px;
+ left: 50%;
+ background: rgba(0,0,0,0.8) !important;
+ padding: 15px 0;
+}
diff --git a/src/emulator.js b/src/emulator.js
index a246e50..6ebd9dd 100644
--- a/src/emulator.js
+++ b/src/emulator.js
@@ -68,6 +68,7 @@ class EmulatorJS {
}
}
constructor(element, config) {
+ window.EJS_TESTING = this;
this.debug = (window.EJS_DEBUG_XX === true);
this.setElements(element);
this.started = false;
@@ -77,6 +78,7 @@ class EmulatorJS {
this.canvas = this.createElement('canvas');
this.canvas.classList.add('ejs_canvas');
this.bindListeners();
+ this.fullscreen = false;
this.game.classList.add("ejs_game");
@@ -222,10 +224,21 @@ class EmulatorJS {
}
getCore() {
const core = this.config.system;
- //switch case or an object holding this data
- if (core === 'nes') {
- return 'fceumm';
+ const options = {
+ 'nes': 'fceumm',
+ 'snes': 'snes9x',
+ 'atari5200': 'a5200',
+ 'gb': 'gambatte',
+ 'gba': 'mgba',
+ 'vb': 'beetle_vb',
+ 'n64': 'mupen64plus_next',
+ 'nds': 'desmume2015',
+ 'mame2003': 'mame2003',
+ 'arcade': 'fbalpha2012_cps1', // I need to find a more compatible arcade core
+ 'psx': 'mednafen_psx_hw',
+ '3do': 'opera'
}
+ return options[core] || core;
}
downloadRom() {
this.gameManager = new window.EJS_GameManager(this.Module);
@@ -286,23 +299,25 @@ class EmulatorJS {
const args = [];
if (this.debug) args.push('-v');
args.push('/game');
- Module.callMain(args);
- Module.resumeMainLoop();
+ this.Module.callMain(args);
+ this.Module.resumeMainLoop();
this.started = true;
this.paused = false;
- Module.setCanvasSize(800, 600);
- let i=0;
- // this needs to be fixed. Ugh.
- let j = setInterval(function() { // some cores have a messed up screen size on load (for example - gba)
- if (i>20) clearInterval(j);
- i++;
- Module.setCanvasSize(800, 600);
+
+ //this needs to be fixed...
+ setInterval(() => {
+ if (document.fullscreenElement !== null) {
+ this.Module.setCanvasSize(this.canvas.getBoundingClientRect().width-10, this.canvas.getBoundingClientRect().height-10);
+ } else {
+ this.Module.setCanvasSize(800, 600);
+ }
}, 100)
}
bindListeners() {
this.createContextMenu();
this.createBottomMenuBar();
this.createControlSettingMenu();
+ this.addEventListener(document, "keydown keyup", this.keyChange.bind(this));
//keyboard, etc...
}
createContextMenu() {
@@ -411,8 +426,9 @@ class EmulatorJS {
popup.appendChild(button);
}
if (!hidden) {
- popup.style.display = "none";
this.currentPopup = popup;
+ } else {
+ popup.style.display = "none";
}
return main;
@@ -478,6 +494,7 @@ class EmulatorJS {
if (callback instanceof Function) {
this.addEventListener(button, 'click', callback);
}
+ return button;
}
//todo. Center text on not restart button
@@ -485,6 +502,26 @@ class EmulatorJS {
addButton("Restart", '', () => {
this.gameManager.restart();
});
+ const pauseButton = addButton("Pause", '', () => {
+ this.togglePlaying();
+ });
+ const playButton = addButton("Play", '', () => {
+ this.togglePlaying();
+ });
+ playButton.style.display = "none";
+ this.togglePlaying = () => {
+ this.paused = !this.paused;
+ if (this.paused) {
+ pauseButton.style.display = "none";
+ playButton.style.display = "";
+ } else {
+ pauseButton.style.display = "";
+ playButton.style.display = "none";
+ }
+ this.gameManager.toggleMainLoop(this.paused ? 0 : 1);
+ }
+
+
let stateUrl;
addButton("Save State", '', async () => {
if (stateUrl) URL.revokeObjectURL(stateUrl);
@@ -505,13 +542,47 @@ class EmulatorJS {
this.controlMenu.style.display = "";
});
+ const spacer = this.createElement("span");
+ spacer.style = "flex:1;";
+ this.elements.menu.appendChild(spacer);
+
+
+ const enter = addButton("Enter Fullscreen", '', () => {
+ if (this.elements.parent.requestFullscreen) {
+ this.elements.parent.requestFullscreen();
+ } else if (this.elements.parent.mozRequestFullScreen) {
+ this.elements.parent.mozRequestFullScreen();
+ } else if (this.elements.parent.webkitRequestFullscreen) {
+ this.elements.parent.webkitRequestFullscreen();
+ } else if (this.elements.parent.msRequestFullscreen) {
+ this.elements.parent.msRequestFullscreen();
+ }
+ exit.style.display = "";
+ enter.style.display = "none";
+ });
+ const exit = addButton("Exit Fullscreen", '', () => {
+ if (document.exitFullscreen) {
+ document.exitFullscreen();
+ } else if (document.webkitExitFullscreen) {
+ document.webkitExitFullscreen();
+ } else if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else if (document.msExitFullscreen) {
+ document.msExitFullscreen();
+ }
+ exit.style.display = "none";
+ enter.style.display = "";
+ });
+ exit.style.display = "none";
+
}
createControlSettingMenu() {
+ this.controls = this.defaultControllers;
const body = this.createPopup("Control Settings", {
"Close": () => {
this.controlMenu.style.display = "none";
}
- });
+ }, true);
this.controlMenu = body.parentElement;
body.classList.add("ejs_control_body");
@@ -546,6 +617,7 @@ class EmulatorJS {
}
let selectedPlayer;
let players = [];
+ let playerDivs = [];
const playerSelect = this.createElement("ul");
playerSelect.classList.add("ejs_control_player_bar");
@@ -559,10 +631,13 @@ class EmulatorJS {
player.setAttribute("aria-controls", "controls-"+(i-1));
player.setAttribute("aria-selected", "false");
player.id = "controls-"+(i-1)+"-label";
- this.addEventListener(player, "click", () => {
+ this.addEventListener(player, "click", (e) => {
+ e.preventDefault();
players[selectedPlayer].classList.remove("ejs_control_selected");
+ playerDivs[selectedPlayer].setAttribute("hidden", "");
selectedPlayer = i-1;
players[i-1].classList.add("ejs_control_selected");
+ playerDivs[i-1].removeAttribute("hidden");
})
playerContainer.appendChild(player);
playerSelect.appendChild(playerContainer);
@@ -570,10 +645,244 @@ class EmulatorJS {
}
body.appendChild(playerSelect);
+
+ const controls = this.createElement("div");
+ for (let i=0; i<4; i++) {
+ const player = this.createElement("div");
+ const playerTitle = this.createElement("div");
+
+ const gamepadTitle = this.createElement("div");
+ gamepadTitle.style = "font-size:12px;";
+ gamepadTitle.innerText = "Connected Gamepad: ";
+
+ const gamepadName = this.createElement("span");
+ gamepadName.innerText = "n/a";
+ gamepadTitle.appendChild(gamepadName);
+
+ const leftPadding = this.createElement("div");
+ leftPadding.style = "width:25%;float:left;";
+ leftPadding.innerHTML = " ";
+
+ const aboutParent = this.createElement("div");
+ aboutParent.style = "font-size:12px;width:50%;float:left;";
+ const gamepad = this.createElement("div");
+ gamepad.style = "text-align:center;width:50%;float:left;";
+ gamepad.innerText = "Gamepad";
+ aboutParent.appendChild(gamepad);
+ const keyboard = this.createElement("div");
+ keyboard.style = "text-align:center;width:50%;float:left;";
+ keyboard.innerText = "Keyboard";
+ aboutParent.appendChild(keyboard);
+
+ const headingPadding = this.createElement("div");
+ headingPadding.style = "clear:both;";
+
+ playerTitle.appendChild(gamepadTitle);
+ playerTitle.appendChild(leftPadding);
+ playerTitle.appendChild(aboutParent);
+ playerTitle.appendChild(headingPadding);
+
+
+ player.appendChild(playerTitle);
+
+ for (const k in buttons) {
+ const buttonText = this.createElement("div");
+ buttonText.setAttribute("data-id", k);
+ buttonText.setAttribute("data-index", i);
+ buttonText.setAttribute("data-label", buttons[k]);
+ buttonText.style = "margin-bottom:10px;";
+ buttonText.classList.add("ejs_control_bar");
+
+
+ const title = this.createElement("div");
+ title.style = "width:25%;float:left;font-size:12px;";
+ const label = this.createElement("label");
+ label.innerText = buttons[k]+":";
+ title.appendChild(label);
+
+ const textBoxes = this.createElement("div");
+ textBoxes.style = "width:50%;float:left;";
+
+ const textBox1Parent = this.createElement("div");
+ textBox1Parent.style = "width:50%;float:left;padding: 0 5px;";
+ const textBox1 = this.createElement("input");
+ textBox1.style = "text-align:center;height:25px;width: 100%;";
+ textBox1.type = "text";
+ textBox1.setAttribute("readonly", "");
+ textBox1.setAttribute("placeholder", "");
+ textBox1Parent.appendChild(textBox1);
+
+ const textBox2Parent = this.createElement("div");
+ textBox2Parent.style = "width:50%;float:left;padding: 0 5px;";
+ const textBox2 = this.createElement("input");
+ textBox2.style = "text-align:center;height:25px;width: 100%;";
+ textBox2.type = "text";
+ textBox2.setAttribute("readonly", "");
+ textBox2.setAttribute("placeholder", "");
+ textBox2Parent.appendChild(textBox2);
+
+ if (this.controls[i][k] && this.controls[i][k].value) {
+ textBox2.value = this.controls[i][k].value;
+ }
+ if (this.controls[i][k] && this.controls[i][k].value2) {
+ textBox1.value = this.controls[i][k].value2;
+ }
+
+ textBoxes.appendChild(textBox1Parent);
+ textBoxes.appendChild(textBox2Parent);
+
+ const padding = this.createElement("div");
+ padding.style = "clear:both;";
+ textBoxes.appendChild(padding);
+
+ const setButton = this.createElement("div");
+ setButton.style = "width:25%;float:left;";
+ const button = this.createElement("button");
+ button.innerText = "Set";
+ setButton.appendChild(button);
+
+ const padding2 = this.createElement("div");
+ padding2.style = "clear:both;";
+
+ buttonText.appendChild(title);
+ buttonText.appendChild(textBoxes);
+ buttonText.appendChild(setButton);
+ buttonText.appendChild(padding2);
+
+ player.appendChild(buttonText);
+
+ this.addEventListener(buttonText, "mousedown", (e) => {
+ e.preventDefault();
+ this.controlPopup.parentElement.removeAttribute("hidden");
+ this.controlPopup.innerText = "[ " + buttons[k] + " ]\ntest";
+ this.controlPopup.setAttribute("button-num", k);
+ this.controlPopup.setAttribute("player-num", i);
+ this.updateTextBoxes = () => {
+ if (this.controls[i][k] && this.controls[i][k].value) {
+ textBox2.value = this.controls[i][k].value;
+ }
+ if (this.controls[i][k] && this.controls[i][k].value2) {
+ textBox1.value = this.controls[i][k].value2;
+ }
+ delete this.updateTextBoxes;
+ }
+ })
+ }
+ controls.appendChild(player);
+ player.setAttribute("hidden", "");
+ playerDivs.push(player);
+ }
+ body.appendChild(controls);
+
+
selectedPlayer = 0;
players[0].classList.add("ejs_control_selected");
+ playerDivs[0].removeAttribute("hidden");
+ const popup = this.createElement('div');
+ popup.classList.add("ejs_popup_container");
+ const popupMsg = this.createElement("div");
+ popupMsg.classList.add("ejs_popup_box");
+ popupMsg.innerText = "yes";
+ popup.setAttribute("hidden", "");
+ this.controlPopup = popupMsg;
+ popup.appendChild(popupMsg);
+ this.controlMenu.appendChild(popup);
+ }
+ defaultControllers = {
+ 0: {
+ 0: {
+ 'value': 'x'
+ },
+ 1: {
+ 'value': 's'
+ },
+ 2: {
+ 'value': 'v'
+ },
+ 3: {
+ 'value': 'enter'
+ },
+ 4: {
+ 'value': 'arrowup'
+ },
+ 5: {
+ 'value': 'arrowdown'
+ },
+ 6: {
+ 'value': 'arrowleft'
+ },
+ 7: {
+ 'value': 'arrowright'
+ },
+ 8: {
+ 'value': 'z'
+ },
+ 9: {
+ 'value': 'a'
+ },
+ 10: {
+ 'value': 'q'
+ },
+ 11: {
+ 'value': 'e'
+ },
+ 12: {
+ 'value': 'e'
+ },
+ 13: {
+ 'value': 'w'
+ },
+ 14: {},
+ 15: {},
+ 16: {
+ 'value': 'h'
+ },
+ 17: {
+ 'value': 'f'
+ },
+ 18: {
+ 'value': 'g'
+ },
+ 19: {
+ 'value': 't'
+ },
+ 20: {'value': 'l'},
+ 21: {'value': 'j'},
+ 22: {'value': 'k'},
+ 23: {'value': 'i'},
+ 24: {},
+ 25: {},
+ 26: {}
+ },
+ 1: {},
+ 2: {},
+ 3: {}
+ }
+ controls;
+ keyChange(e) {
+ if (!this.started) return;
+ e.preventDefault();
+ if (this.controlPopup.parentElement.getAttribute("hidden") === null) {
+ const num = this.controlPopup.getAttribute("button-num");
+ const player = this.controlPopup.getAttribute("player-num");
+ if (!this.controls[player][num]) {
+ this.controls[player][num] = {};
+ }
+ this.controls[player][num].value = e.key.toLowerCase();
+ this.controlPopup.parentElement.setAttribute("hidden", "");
+ this.updateTextBoxes();
+ return;
+ }
+ const special = [16, 17, 18, 19, 20, 21, 22, 23];
+ for (let i=0; i<4; i++) {
+ for (let j=0; j<26; j++) {
+ if (this.controls[i][j] && this.controls[i][j].value === e.key.toLowerCase()) {
+ this.gameManager.simulateInput(i, j, (e.type === 'keyup' ? 0 : (special.includes(j) ? 0x7fff : 1)));
+ }
+ }
+ }
+
}
-
}