@@ -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