Skip to content

Commit 6e1fde1

Browse files
authored
Merge pull request #1182 from chrisallenlane/feat/controller-port-device
feat: add controller port device type selector
2 parents cb1b844 + b929a85 commit 6e1fde1

2 files changed

Lines changed: 77 additions & 2 deletions

File tree

data/src/GameManager.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class EJS_GameManager {
3232
setVSync: this.Module.cwrap("set_vsync", "null", ["number"]),
3333
setVideoRoation: this.Module.cwrap("set_video_rotation", "null", ["number"]),
3434
getVideoDimensions: this.Module.cwrap("get_video_dimensions", "number", ["string"]),
35-
setKeyboardEnabled: this.Module.cwrap("ejs_set_keyboard_enabled", "null", ["number"])
35+
setKeyboardEnabled: this.Module.cwrap("ejs_set_keyboard_enabled", "null", ["number"]),
36+
setControllerPortDevice: this.Module.cwrap("ejs_set_controller_port_device", "null", ["number", "number"]),
37+
getControllerPortInfo: this.Module.cwrap("ejs_get_controller_port_info", "string", [])
3638
}
3739

3840
this.writeFile("/home/web_user/.config/retroarch/retroarch.cfg", this.getRetroArchCfg());
@@ -417,6 +419,12 @@ IF EXIST AUTORUN.BAT CALL AUTORUN.BAT
417419
supportsStates() {
418420
return !!this.functions.supportsStates();
419421
}
422+
setControllerPortDevice(port, device) {
423+
this.functions.setControllerPortDevice(port, device);
424+
}
425+
getControllerPortInfo() {
426+
return this.functions.getControllerPortInfo();
427+
}
420428
getSaveFile(save) {
421429
if (save !== false) {
422430
this.saveSaveFiles();

data/src/emulator.js

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,7 @@ class EmulatorJS {
16161616
this.elements.contextmenu.classList.add("ejs_context_menu");
16171617
this.addEventListener(this.game, "contextmenu", (e) => {
16181618
e.preventDefault();
1619-
if ((this.config.buttonOpts && this.config.buttonOpts.rightClick === false) || !this.started) return;
1619+
if ((this.config.buttonOpts && this.config.buttonOpts.rightClick === false) || !this.started || this.lightgunActive) return;
16201620
const parentRect = this.elements.parent.getBoundingClientRect();
16211621
this.elements.contextmenu.style.display = "block";
16221622
const rect = this.elements.contextmenu.getBoundingClientRect();
@@ -1631,6 +1631,19 @@ class EmulatorJS {
16311631
this.addEventListener(this.elements.contextmenu, "contextmenu", (e) => e.preventDefault());
16321632
this.addEventListener(this.elements.parent, "contextmenu", (e) => e.preventDefault());
16331633
this.addEventListener(this.game, "mousedown touchend", hideMenu);
1634+
// Prevent mouse buttons 4/5 (back/forward) from navigating away
1635+
// when used as lightgun Start/Select. Works in Chromium-based
1636+
// browsers; Firefox handles back/forward navigation before page
1637+
// event handlers fire, so this has no effect there.
1638+
// See: https://support.mozilla.org/en-US/questions/1319892
1639+
for (const evtName of ["mousedown", "mouseup", "auxclick"]) {
1640+
this.addEventListener(this.game, evtName, (e) => {
1641+
if (this.lightgunActive && (e.button === 3 || e.button === 4)) {
1642+
e.preventDefault();
1643+
e.stopPropagation();
1644+
}
1645+
});
1646+
}
16341647
const parent = this.createElement("ul");
16351648
const addButton = (title, hidden, functi0n) => {
16361649
//<li><a href="#" onclick="return false">'+title+'</a></li>
@@ -4832,6 +4845,27 @@ class EmulatorJS {
48324845
this.enableMouseLock = (value === "enabled");
48334846
} else if (option === "autofireInterval") {
48344847
this.defaultAutoFireInterval = parseInt(value);
4848+
} else if (option.startsWith("controller-port-device-p")) {
4849+
const port = parseInt(option.replace("controller-port-device-p", "")) - 1;
4850+
const deviceId = parseInt(value);
4851+
this.gameManager.setControllerPortDevice(port, deviceId);
4852+
/* RETRO_DEVICE_LIGHTGUN = 4; subclass mask = 0xFF */
4853+
const isLightgun = (deviceId & 0xFF) === 4;
4854+
if (isLightgun) {
4855+
this.lightgunActive = true;
4856+
} else {
4857+
/* Re-check all ports */
4858+
this.lightgunActive = false;
4859+
for (const k in this.allSettings) {
4860+
if (k.startsWith("controller-port-device-p")) {
4861+
const v = parseInt(this.allSettings[k]);
4862+
if ((v & 0xFF) === 4) this.lightgunActive = true;
4863+
}
4864+
}
4865+
}
4866+
if (this.canvas) {
4867+
this.canvas.style.cursor = this.lightgunActive ? "none" : "";
4868+
}
48354869
}
48364870
}
48374871
menuOptionChanged(option, value) {
@@ -5507,6 +5541,39 @@ class EmulatorJS {
55075541

55085542
checkForEmptyMenu(inputOptions);
55095543

5544+
let controllerPortInfo;
5545+
try {
5546+
controllerPortInfo = this.gameManager.getControllerPortInfo();
5547+
} catch(e) {
5548+
if (this.debug) console.warn("getControllerPortInfo not available:", e);
5549+
}
5550+
if (controllerPortInfo) {
5551+
// Parse the port info: each line is "port:deviceId:description"
5552+
const ports = {};
5553+
controllerPortInfo.split("\n").forEach(line => {
5554+
if (!line.trim()) return;
5555+
const parts = line.split(":");
5556+
if (parts.length < 3) return;
5557+
const port = parseInt(parts[0]);
5558+
const deviceId = parts[1];
5559+
const desc = parts.slice(2).join(":");
5560+
if (!ports[port]) ports[port] = {};
5561+
ports[port][deviceId] = this.localization(desc);
5562+
});
5563+
const portKeys = Object.keys(ports);
5564+
if (portKeys.length > 0) {
5565+
const controllerDeviceOpts = createSettingParent(true, "Controller Port Devices", home);
5566+
for (const port of portKeys) {
5567+
const portNum = parseInt(port) + 1;
5568+
if (Object.keys(ports[port]).length <= 1) continue;
5569+
addToMenu(this.localization("Port") + " " + portNum,
5570+
"controller-port-device-p" + portNum,
5571+
ports[port], "1", controllerDeviceOpts, true);
5572+
}
5573+
checkForEmptyMenu(controllerDeviceOpts);
5574+
}
5575+
}
5576+
55105577
if (this.saveInBrowserSupported()) {
55115578
const saveStateOpts = createSettingParent(true, "Save States", home);
55125579
addToMenu(this.localization("Save State Slot"), "save-state-slot", ["1", "2", "3", "4", "5", "6", "7", "8", "9"], "1", saveStateOpts, true);

0 commit comments

Comments
 (0)