2017-12-28 16:40:15 +00:00
/ *
* Copyright ( C ) 2012 Brendan Robert ( BLuRry ) brendan . robert @gmail.com.
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2 . 1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 USA
* /
package jace.core ;
2023-07-03 20:44:23 +00:00
import java.awt.Toolkit ;
2017-12-28 16:40:15 +00:00
import java.awt.datatransfer.Clipboard ;
import java.awt.datatransfer.DataFlavor ;
import java.awt.datatransfer.UnsupportedFlavorException ;
import java.io.IOException ;
import java.io.StringReader ;
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
import java.lang.reflect.Modifier ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.Map ;
import java.util.Set ;
import java.util.logging.Level ;
import java.util.logging.Logger ;
2023-07-03 20:44:23 +00:00
import jace.Emulator ;
import jace.apple2e.SoftSwitches ;
import jace.config.InvokableAction ;
import jace.config.Reconfigurable ;
import javafx.event.EventHandler ;
import javafx.scene.input.KeyCode ;
import javafx.scene.input.KeyEvent ;
2017-12-28 16:40:15 +00:00
/ * *
* Keyboard manages all keyboard - related activities . For now , all hotkeys are
* hard - coded . The eventual direction for this class is to only manage key
* handlers for all keys and provide remapping - - but it ' s not there yet .
* Created on March 29 , 2007 , 11 : 32 PM
*
* @author Brendan Robert ( BLuRry ) brendan . robert @gmail.com
* /
public class Keyboard implements Reconfigurable {
public void resetState ( ) {
clearStrobe ( ) ;
openApple ( false ) ;
solidApple ( false ) ;
}
private Computer computer ;
public Keyboard ( Computer computer ) {
this . computer = computer ;
}
@Override
public String getShortName ( ) {
return " kbd " ;
}
static byte currentKey = 0 ;
2017-12-29 19:46:27 +00:00
public boolean shiftPressed = false ;
2017-12-28 16:40:15 +00:00
public static void clearStrobe ( ) {
currentKey = ( byte ) ( currentKey & 0x07f ) ;
}
public static void pressKey ( byte key ) {
currentKey = ( byte ) ( 0x0ff & ( 0x080 | key ) ) ;
}
public static byte readState ( ) {
// If strobe was cleared...
if ( ( currentKey & 0x080 ) = = 0 ) {
// Call clipboard buffer paste routine
int newKey = Keyboard . getClipboardKeystroke ( ) ;
if ( newKey > = 0 ) {
pressKey ( ( byte ) newKey ) ;
}
}
return currentKey ;
}
/ * *
* Creates a new instance of Keyboard
* /
public Keyboard ( ) {
}
2021-11-09 17:20:19 +00:00
private static final Map < KeyCode , Set < KeyHandler > > keyHandlersByKey = new HashMap < > ( ) ;
private static final Map < Object , Set < KeyHandler > > keyHandlersByOwner = new HashMap < > ( ) ;
2017-12-28 16:40:15 +00:00
public static void registerInvokableAction ( InvokableAction action , Object owner , Method method , String code ) {
boolean isStatic = Modifier . isStatic ( method . getModifiers ( ) ) ;
registerKeyHandler ( new KeyHandler ( code ) {
@Override
public boolean handleKeyUp ( KeyEvent e ) {
2023-07-03 20:44:23 +00:00
Emulator . withComputer ( c - > c . getKeyboard ( ) . shiftPressed = e . isShiftDown ( ) ) ;
2017-12-28 16:40:15 +00:00
if ( action = = null | | ! action . notifyOnRelease ( ) ) {
return false ;
}
// System.out.println("Key up: "+method.toString());
Object returnValue = null ;
try {
if ( method . getParameterCount ( ) > 0 ) {
returnValue = method . invoke ( isStatic ? null : owner , false ) ;
} else {
returnValue = method . invoke ( isStatic ? null : owner ) ;
}
} catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
Logger . getLogger ( Keyboard . class . getName ( ) ) . log ( Level . SEVERE , null , ex ) ;
}
if ( returnValue ! = null ) {
return ( Boolean ) returnValue ;
}
return action . consumeKeyEvent ( ) ;
}
@Override
public boolean handleKeyDown ( KeyEvent e ) {
// System.out.println("Key down: "+method.toString());
2023-07-03 20:44:23 +00:00
Emulator . withComputer ( c - > c . getKeyboard ( ) . shiftPressed = e . isShiftDown ( ) ) ;
2017-12-28 16:40:15 +00:00
Object returnValue = null ;
try {
if ( method . getParameterCount ( ) > 0 ) {
returnValue = method . invoke ( isStatic ? null : owner , true ) ;
} else {
returnValue = method . invoke ( isStatic ? null : owner ) ;
}
} catch ( IllegalAccessException | IllegalArgumentException | InvocationTargetException ex ) {
Logger . getLogger ( Keyboard . class . getName ( ) ) . log ( Level . SEVERE , null , ex ) ;
}
if ( returnValue ! = null ) {
return ( Boolean ) returnValue ;
}
return action ! = null ? action . consumeKeyEvent ( ) : null ;
}
} , owner ) ;
}
public static void registerKeyHandler ( KeyHandler l , Object owner ) {
if ( ! keyHandlersByKey . containsKey ( l . key ) ) {
keyHandlersByKey . put ( l . key , new HashSet < > ( ) ) ;
}
keyHandlersByKey . get ( l . key ) . add ( l ) ;
if ( ! keyHandlersByOwner . containsKey ( owner ) ) {
keyHandlersByOwner . put ( owner , new HashSet < > ( ) ) ;
}
keyHandlersByOwner . get ( owner ) . add ( l ) ;
// System.out.println("Registered handler for "+l.getComboName()+"; code is "+l.getKeyName());
}
public static void unregisterAllHandlers ( Object owner ) {
if ( ! keyHandlersByOwner . containsKey ( owner ) ) {
return ;
}
2021-11-09 17:20:19 +00:00
keyHandlersByOwner . get ( owner ) . stream ( ) . filter ( ( handler ) - > keyHandlersByKey . containsKey ( handler . key ) ) . forEach (
( handler ) - > keyHandlersByKey . get ( handler . key ) . remove ( handler ) ) ;
2017-12-28 16:40:15 +00:00
keyHandlersByOwner . remove ( owner ) ;
}
public static void processKeyDownEvents ( KeyEvent e ) {
if ( keyHandlersByKey . containsKey ( e . getCode ( ) ) ) {
for ( KeyHandler h : keyHandlersByKey . get ( e . getCode ( ) ) ) {
if ( ! h . match ( e ) ) {
continue ;
}
boolean isHandled = h . handleKeyDown ( e ) ;
if ( isHandled ) {
e . consume ( ) ;
return ;
}
}
}
}
public static void processKeyUpEvents ( KeyEvent e ) {
if ( keyHandlersByKey . containsKey ( e . getCode ( ) ) ) {
for ( KeyHandler h : keyHandlersByKey . get ( e . getCode ( ) ) ) {
if ( ! h . match ( e ) ) {
continue ;
}
boolean isHandled = h . handleKeyUp ( e ) ;
if ( isHandled ) {
e . consume ( ) ;
return ;
}
}
}
}
public EventHandler < KeyEvent > getListener ( ) {
return ( KeyEvent event ) - > {
if ( event . getEventType ( ) = = KeyEvent . KEY_PRESSED ) {
keyPressed ( event ) ;
} else if ( event . getEventType ( ) = = KeyEvent . KEY_RELEASED ) {
keyReleased ( event ) ;
}
} ;
}
public void keyPressed ( KeyEvent e ) {
processKeyDownEvents ( e ) ;
if ( e . isConsumed ( ) ) {
return ;
}
char c = 255 ;
if ( e . getText ( ) . length ( ) > 0 ) {
c = e . getText ( ) . charAt ( 0 ) ;
}
switch ( e . getCode ( ) ) {
case LEFT :
case KP_LEFT :
c = 8 ;
break ;
case RIGHT :
case KP_RIGHT :
c = 21 ;
break ;
case UP :
case KP_UP :
c = 11 ;
break ;
case DOWN :
case KP_DOWN :
c = 10 ;
break ;
case ESCAPE :
c = 27 ;
break ;
case TAB :
c = 9 ;
break ;
case ENTER :
c = 13 ;
break ;
case BACK_SPACE :
c = 127 ;
break ;
default :
}
2023-07-03 20:44:23 +00:00
Emulator . withComputer ( compter - > computer . getKeyboard ( ) . shiftPressed = e . isShiftDown ( ) ) ;
2017-12-28 16:40:15 +00:00
if ( e . isShiftDown ( ) ) {
c = fixShiftedChar ( c ) ;
}
if ( e . isControlDown ( ) ) {
if ( c = = 255 ) {
return ;
}
c = ( char ) ( c & 0x01f ) ;
}
if ( c < 128 ) {
pressKey ( ( byte ) c ) ;
}
}
private char fixShiftedChar ( char c ) {
if ( c > = 'a' & & c < = 'z' ) {
return ( char ) ( c - 32 ) ;
} else {
switch ( c ) {
case '0' : return ')' ;
case '1' : return '!' ;
case '2' : return '@' ;
case '3' : return '#' ;
case '4' : return '$' ;
case '5' : return '%' ;
case '6' : return '^' ;
case '7' : return '&' ;
case '8' : return '*' ;
case '9' : return '(' ;
case '-' : return '_' ;
case '=' : return '+' ;
case '[' : return '{' ;
case ']' : return '}' ;
case '\\' : return '|' ;
case ';' : return ':' ;
case '\'' : return '"' ;
case ',' : return '<' ;
case '.' : return '>' ;
case '/' : return '?' ;
case '`' : return '~' ;
}
}
return c ;
}
public void keyReleased ( KeyEvent e ) {
KeyCode code = e . getCode ( ) ;
processKeyUpEvents ( e ) ;
if ( code = = null | | e . isConsumed ( ) ) {
return ;
}
e . consume ( ) ;
}
@InvokableAction ( name = " Open Apple Key " , alternatives = " OA " , category = " Keyboard " , notifyOnRelease = true , defaultKeyMapping = " Alt " , consumeKeyEvent = false )
public void openApple ( boolean pressed ) {
2023-07-03 20:44:23 +00:00
// boolean isRunning = computer.pause();
2017-12-28 16:40:15 +00:00
SoftSwitches . PB0 . getSwitch ( ) . setState ( pressed ) ;
2023-07-03 20:44:23 +00:00
// if (isRunning) {
// computer.resume();
// }
2017-12-28 16:40:15 +00:00
}
@InvokableAction ( name = " Closed Apple Key " , alternatives = " CA " , category = " Keyboard " , notifyOnRelease = true , defaultKeyMapping = { " Shortcut " , " Meta " , " Command " } , consumeKeyEvent = false )
public void solidApple ( boolean pressed ) {
2023-07-03 20:44:23 +00:00
// boolean isRunning = computer.pause();
2017-12-28 16:40:15 +00:00
SoftSwitches . PB1 . getSwitch ( ) . setState ( pressed ) ;
2023-07-03 20:44:23 +00:00
// if (isRunning) {
// computer.resume();
// }
2017-12-28 16:40:15 +00:00
}
public static void pasteFromString ( String text ) {
text = text . replaceAll ( " \\ r? \\ n| \\ r " , ( char ) 0x0d + " " ) ;
pasteBuffer = new StringReader ( text ) ;
}
@InvokableAction ( name = " Paste clipboard " , alternatives = " paste " , category = " Keyboard " , notifyOnRelease = false , defaultKeyMapping = { " Ctrl+Shift+V " , " Shift+Insert " } , consumeKeyEvent = true )
public static void pasteFromClipboard ( ) {
try {
Clipboard clip = Toolkit . getDefaultToolkit ( ) . getSystemClipboard ( ) ;
String contents = ( String ) clip . getData ( DataFlavor . stringFlavor ) ;
if ( contents ! = null & & ! " " . equals ( contents ) ) {
contents = contents . replaceAll ( " \\ r? \\ n| \\ r " , ( char ) 0x0d + " " ) ;
pasteBuffer = new StringReader ( contents ) ;
}
} catch ( UnsupportedFlavorException | IOException ex ) {
Logger . getLogger ( Keyboard . class
. getName ( ) ) . log ( Level . SEVERE , null , ex ) ;
}
}
static StringReader pasteBuffer = null ;
public static int getClipboardKeystroke ( ) {
if ( pasteBuffer = = null ) {
return - 1 ;
}
try {
int keypress = pasteBuffer . read ( ) ;
// Handle end of paste buffer
if ( keypress = = - 1 ) {
pasteBuffer . close ( ) ;
pasteBuffer = null ;
return - 1 ;
}
return ( keypress & 0x0ff ) ;
} catch ( IOException ex ) {
Logger . getLogger ( Keyboard . class
. getName ( ) ) . log ( Level . SEVERE , null , ex ) ;
}
return - 1 ;
}
@Override
public String getName ( ) {
return " Keyboard " ;
}
@Override
public void reconfigure ( ) {
}
}