forked from Apple-2-Tools/jace
Completely reworked how hotkeys are handled, and now invokable actions are a real concept in the emulator!
This commit is contained in:
parent
af8466ca54
commit
a477abac03
@ -34,6 +34,7 @@ import java.util.Map;
|
||||
public class Emulator {
|
||||
|
||||
public static Emulator instance;
|
||||
public static EmulatorUILogic logic = new EmulatorUILogic();
|
||||
public static Thread mainThread;
|
||||
|
||||
// public static void main(String... args) {
|
||||
|
@ -23,6 +23,7 @@ import jace.apple2e.RAM128k;
|
||||
import jace.apple2e.SoftSwitches;
|
||||
import jace.config.ConfigurationPanel;
|
||||
import jace.config.InvokableAction;
|
||||
import jace.config.Reconfigurable;
|
||||
import jace.core.CPU;
|
||||
import jace.core.Debugger;
|
||||
import jace.core.RAM;
|
||||
@ -63,7 +64,7 @@ import javax.swing.JTextField;
|
||||
*
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public class EmulatorUILogic {
|
||||
public class EmulatorUILogic implements Reconfigurable {
|
||||
|
||||
static Debugger debugger;
|
||||
|
||||
@ -193,7 +194,8 @@ public class EmulatorUILogic {
|
||||
name = "BRUN file",
|
||||
category = "file",
|
||||
description = "Loads a binary file in memory and executes it. File should end with #06xxxx, where xxxx is the start address in hex",
|
||||
alternatives = "Execute program;Load binary;Load program;Load rom;Play single-load game")
|
||||
alternatives = "Execute program;Load binary;Load program;Load rom;Play single-load game",
|
||||
defaultKeyMapping = "ctrl+shift+b")
|
||||
public static void runFile() {
|
||||
Emulator.computer.pause();
|
||||
JFileChooser select = new JFileChooser();
|
||||
@ -243,7 +245,9 @@ public class EmulatorUILogic {
|
||||
name = "Adjust display",
|
||||
category = "display",
|
||||
description = "Adjusts window size to 1:1 aspect ratio for optimal viewing.",
|
||||
alternatives = "Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;")
|
||||
alternatives = "Adjust screen;Adjust window size;Adjust aspect ratio;Fix screen;Fix window size;Fix aspect ratio;Correct aspect ratio;",
|
||||
defaultKeyMapping = "ctrl+shift+a")
|
||||
|
||||
static public void scaleIntegerRatio() {
|
||||
// AbstractEmulatorFrame frame = Emulator.getFrame();
|
||||
// if (frame == null) {
|
||||
@ -258,7 +262,8 @@ public class EmulatorUILogic {
|
||||
name = "Toggle Debug",
|
||||
category = "debug",
|
||||
description = "Show/hide the debug panel",
|
||||
alternatives = "Show Debug;Hide Debug")
|
||||
alternatives = "Show Debug;Hide Debug",
|
||||
defaultKeyMapping = "ctrl+shift+d")
|
||||
public static void toggleDebugPanel() {
|
||||
// AbstractEmulatorFrame frame = Emulator.getFrame();
|
||||
// if (frame == null) {
|
||||
@ -283,7 +288,8 @@ public class EmulatorUILogic {
|
||||
name = "Save Raw Screenshot",
|
||||
category = "general",
|
||||
description = "Save raw (RAM) format of visible screen",
|
||||
alternatives = "screendump, raw screenshot")
|
||||
alternatives = "screendump, raw screenshot",
|
||||
defaultKeyMapping = "ctrl+shift+z")
|
||||
public static void saveScreenshotRaw() throws FileNotFoundException, IOException {
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss");
|
||||
String timestamp = df.format(new Date());
|
||||
@ -323,7 +329,8 @@ public class EmulatorUILogic {
|
||||
name = "Save Screenshot",
|
||||
category = "general",
|
||||
description = "Save image of visible screen",
|
||||
alternatives = "Save image,save framebuffer,screenshot")
|
||||
alternatives = "Save image,save framebuffer,screenshot",
|
||||
defaultKeyMapping = "ctrl+shift+s")
|
||||
public static void saveScreenshot() throws HeadlessException, IOException {
|
||||
JFileChooser select = new JFileChooser();
|
||||
Emulator.computer.pause();
|
||||
@ -352,7 +359,8 @@ public class EmulatorUILogic {
|
||||
name = "Configuration",
|
||||
category = "general",
|
||||
description = "Edit emulator configuraion",
|
||||
alternatives = "Reconfigure,Preferences,Settings")
|
||||
alternatives = "Reconfigure,Preferences,Settings",
|
||||
defaultKeyMapping = {"f4","ctrl+shift+c"})
|
||||
public static void showConfig() {
|
||||
// if (Emulator.getFrame().getModalDialogUI(CONFIGURATION_DIALOG_NAME) == null) {
|
||||
// JPanel ui = new ConfigurationPanel();
|
||||
@ -368,7 +376,8 @@ public class EmulatorUILogic {
|
||||
name = "Media Manager",
|
||||
category = "general",
|
||||
description = "Show the media manager",
|
||||
alternatives = "Insert disk;Eject disk;Browse;Download;Select")
|
||||
alternatives = "Insert disk;Eject disk;Browse;Download;Select",
|
||||
defaultKeyMapping = {"f1","ctrl+shift+o"})
|
||||
public static void showMediaManager() {
|
||||
// if (Emulator.getFrame().getModalDialogUI(MEDIA_MANAGER_DIALOG_NAME) == null) {
|
||||
// Emulator.getFrame().registerModalDialog(MediaLibrary.getInstance().buildUserInterface(), MEDIA_MANAGER_DIALOG_NAME, null, false);
|
||||
@ -380,4 +389,18 @@ public class EmulatorUILogic {
|
||||
// return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Emulator.getFrame(), message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Jace User Interface";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return "UI";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconfigure() {
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
package jace.cheat;
|
||||
|
||||
import jace.apple2e.RAM128k;
|
||||
import jace.config.InvokableAction;
|
||||
import jace.core.Computer;
|
||||
import jace.core.KeyHandler;
|
||||
import jace.core.Keyboard;
|
||||
@ -145,6 +146,7 @@ public class MetaCheats extends Cheats {
|
||||
model.addRow(new Object[]{loc, val + " ("+hex(val,4)+")"});
|
||||
});
|
||||
}
|
||||
@InvokableAction(name="Show Cheats", category = "cheats", defaultKeyMapping = "home")
|
||||
public void showCheatForm() {
|
||||
if (form == null) {
|
||||
form = new MetaCheatForm();
|
||||
@ -153,6 +155,7 @@ public class MetaCheats extends Cheats {
|
||||
}
|
||||
|
||||
MemorySpy spy = null;
|
||||
@InvokableAction(name="Show Memory Spy", category = "cheats", defaultKeyMapping = "end")
|
||||
public void showMemorySpy() {
|
||||
if (spy == null) {
|
||||
spy = new MemorySpy();
|
||||
@ -199,32 +202,7 @@ public class MetaCheats extends Cheats {
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Keyboard.registerKeyHandler(new KeyHandler(KeyCode.END) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
showCheatForm();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
return false;
|
||||
}
|
||||
}, this);
|
||||
Keyboard.registerKeyHandler(new KeyHandler(KeyCode.HOME) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
showMemorySpy();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
return false;
|
||||
}
|
||||
}, this);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,7 +19,9 @@
|
||||
package jace.config;
|
||||
|
||||
import jace.Emulator;
|
||||
import jace.EmulatorUILogic;
|
||||
import jace.core.Computer;
|
||||
import jace.core.Keyboard;
|
||||
import jace.core.Utility;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -31,13 +33,14 @@ import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -56,6 +59,15 @@ import javax.swing.tree.TreePath;
|
||||
*/
|
||||
public class Configuration implements Reconfigurable {
|
||||
|
||||
private static Method findAnyMethodByName(Class<? extends Reconfigurable> aClass, String m) {
|
||||
for (Method method : aClass.getMethods()) {
|
||||
if (method.getName().equals(m)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Configuration";
|
||||
}
|
||||
@ -136,8 +148,9 @@ public class Configuration implements Reconfigurable {
|
||||
public transient ConfigNode parent;
|
||||
public transient Reconfigurable subject;
|
||||
private Map<String, Serializable> settings;
|
||||
private Map<String, String[]> hotkeys;
|
||||
protected Map<String, ConfigNode> children;
|
||||
private boolean changed = false;
|
||||
private boolean changed = true;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -155,6 +168,7 @@ public class Configuration implements Reconfigurable {
|
||||
public ConfigNode(ConfigNode parent, Reconfigurable subject) {
|
||||
this.subject = subject;
|
||||
this.settings = new TreeMap<>();
|
||||
this.hotkeys = new TreeMap<>();
|
||||
this.children = new TreeMap<>();
|
||||
this.parent = parent;
|
||||
if (this.parent != null) {
|
||||
@ -189,6 +203,7 @@ public class Configuration implements Reconfigurable {
|
||||
}
|
||||
}
|
||||
public final static ConfigNode BASE;
|
||||
public static EmulatorUILogic ui = Emulator.logic;
|
||||
public static Computer emulator = Emulator.computer;
|
||||
@ConfigurableField(name = "Autosave Changes", description = "If unchecked, changes are only saved when the Save button is pressed.")
|
||||
public static boolean saveAutomatically = false;
|
||||
@ -199,13 +214,20 @@ public class Configuration implements Reconfigurable {
|
||||
}
|
||||
|
||||
public static void buildTree() {
|
||||
buildTree(BASE, new HashSet());
|
||||
buildTree(BASE, new LinkedHashSet());
|
||||
}
|
||||
|
||||
private static void buildTree(ConfigNode node, Set visited) {
|
||||
if (node.subject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Method m : node.subject.getClass().getMethods()) {
|
||||
InvokableAction action = m.getDeclaredAnnotation(InvokableAction.class);
|
||||
if (action == null) continue;
|
||||
node.hotkeys.put(m.getName(), action.defaultKeyMapping());
|
||||
}
|
||||
|
||||
for (Field f : node.subject.getClass().getFields()) {
|
||||
// System.out.println("Evaluating field " + f.getName());
|
||||
try {
|
||||
@ -213,6 +235,7 @@ public class Configuration implements Reconfigurable {
|
||||
if (/*o == null ||*/visited.contains(o)) {
|
||||
continue;
|
||||
}
|
||||
visited.add(o);
|
||||
// System.out.println(o.getClass().getName());
|
||||
// If the object in question is not reconfigurable,
|
||||
// skip over it and investigate its fields instead
|
||||
@ -232,7 +255,6 @@ public class Configuration implements Reconfigurable {
|
||||
|
||||
if (o instanceof Reconfigurable) {
|
||||
Reconfigurable r = (Reconfigurable) o;
|
||||
visited.add(r);
|
||||
ConfigNode child = node.children.get(f.getName());
|
||||
if (child == null || !child.subject.equals(o)) {
|
||||
child = new ConfigNode(node, r);
|
||||
@ -275,7 +297,6 @@ public class Configuration implements Reconfigurable {
|
||||
} else {
|
||||
children = Arrays.asList((Reconfigurable[]) o);
|
||||
}
|
||||
visited.add(o);
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
Reconfigurable child = children.get(i);
|
||||
String childName = fieldName + i;
|
||||
@ -301,7 +322,8 @@ public class Configuration implements Reconfigurable {
|
||||
name = "Save settings",
|
||||
description = "Save all configuration settings as defaults",
|
||||
category = "general",
|
||||
alternatives = "save preferences;save defaults"
|
||||
alternatives = "save preferences;save defaults",
|
||||
defaultKeyMapping = "meta+ctrl+s"
|
||||
)
|
||||
public static void saveSettings() {
|
||||
FileOutputStream fos = null;
|
||||
@ -331,16 +353,19 @@ public class Configuration implements Reconfigurable {
|
||||
name = "Load settings",
|
||||
description = "Load all configuration settings previously saved",
|
||||
category = "general",
|
||||
alternatives = "load preferences;revert settings;revert preferences"
|
||||
alternatives = "load preferences;revert settings;revert preferences",
|
||||
defaultKeyMapping = "meta+ctrl+r"
|
||||
)
|
||||
public static void loadSettings() {
|
||||
{
|
||||
boolean successful = false;
|
||||
ObjectInputStream ois = null;
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
ois = new ObjectInputStream(new FileInputStream(getSettingsFile()));
|
||||
ConfigNode newRoot = (ConfigNode) ois.readObject();
|
||||
applyConfigTree(newRoot, BASE);
|
||||
successful = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
|
||||
} catch (FileNotFoundException ex) {
|
||||
@ -353,6 +378,9 @@ public class Configuration implements Reconfigurable {
|
||||
if (ois != null) {
|
||||
ois.close();
|
||||
}
|
||||
if (!successful) {
|
||||
applySettings(BASE);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Configuration.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
@ -409,6 +437,7 @@ public class Configuration implements Reconfigurable {
|
||||
return;
|
||||
}
|
||||
oldRoot.settings = newRoot.settings;
|
||||
oldRoot.hotkeys = newRoot.hotkeys;
|
||||
if (oldRoot.subject != null) {
|
||||
doApply(oldRoot);
|
||||
buildTree(oldRoot, new HashSet());
|
||||
@ -421,6 +450,17 @@ public class Configuration implements Reconfigurable {
|
||||
|
||||
private static void doApply(ConfigNode node) {
|
||||
List<String> removeList = new ArrayList<>();
|
||||
Keyboard.unregisterAllHandlers(node.subject);
|
||||
for (String m : node.hotkeys.keySet()) {
|
||||
Method method = findAnyMethodByName(node.subject.getClass(), m);
|
||||
if (method == null) continue;
|
||||
InvokableAction action = method.getAnnotation(InvokableAction.class);
|
||||
if (action == null) continue;
|
||||
for (String code : node.hotkeys.get(m)) {
|
||||
Keyboard.registerInvokableAction(action, node.subject, method, code);
|
||||
}
|
||||
}
|
||||
|
||||
for (String f : node.settings.keySet()) {
|
||||
try {
|
||||
Field ff = node.subject.getClass().getField(f);
|
||||
|
@ -69,4 +69,19 @@ public @interface InvokableAction {
|
||||
* reboot might have alternatives as "warm start;cold start;boot;restart".
|
||||
*/
|
||||
public String alternatives() default "";
|
||||
/*
|
||||
* If true, the key event will be consumed and not processed by any other event handlers
|
||||
* If the corresponding method returns a boolean, that value will be used instead.
|
||||
* True = consume (stop processing keystroke), false = pass-through to other handlers
|
||||
*/
|
||||
public boolean consumeKeyEvent() default true;
|
||||
/*
|
||||
* If false (default) event is only triggered on press, not release. If true,
|
||||
* method is notified on press and on release
|
||||
*/
|
||||
public boolean notifyOnRelease() default false;
|
||||
/*
|
||||
* Standard keyboard mapping
|
||||
*/
|
||||
public String[] defaultKeyMapping();
|
||||
}
|
@ -114,14 +114,23 @@ public abstract class Computer implements Reconfigurable {
|
||||
name = "Cold boot",
|
||||
description = "Process startup sequence from power-up",
|
||||
category = "general",
|
||||
alternatives = "Full reset;reset emulator")
|
||||
alternatives = "Full reset;reset emulator",
|
||||
consumeKeyEvent = true,
|
||||
defaultKeyMapping = {"Ctrl+Shift+Backspace","Ctrl+Shift+Delete"})
|
||||
public void invokeColdStart() {
|
||||
coldStart();
|
||||
}
|
||||
public abstract void coldStart();
|
||||
|
||||
@InvokableAction(
|
||||
name = "Warm boot",
|
||||
description = "Process user-initatiated reboot (ctrl+apple+reset)",
|
||||
category = "general",
|
||||
alternatives = "reboot;reset;three-finger-salute")
|
||||
alternatives = "reboot;reset;three-finger-salute",
|
||||
defaultKeyMapping = {"Ctrl+Ignore Alt+Ignore Meta+Backspace","Ctrl+Ignore Alt+Ignore Meta+Delete"})
|
||||
public void invokeWarmStart() {
|
||||
warmStart();
|
||||
}
|
||||
public abstract void warmStart();
|
||||
|
||||
public Keyboard getKeyboard() {
|
||||
@ -134,14 +143,14 @@ public abstract class Computer implements Reconfigurable {
|
||||
|
||||
protected abstract void doResume();
|
||||
|
||||
@InvokableAction(name = "Pause", description = "Stops the computer, allowing reconfiguration of core elements", alternatives = "freeze;halt")
|
||||
@InvokableAction(name = "Pause", description = "Stops the computer, allowing reconfiguration of core elements", alternatives = "freeze;halt", defaultKeyMapping = {"meta+pause","alt+pause"})
|
||||
public boolean pause() {
|
||||
boolean result = isRunning();
|
||||
doPause();
|
||||
return result;
|
||||
}
|
||||
|
||||
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume")
|
||||
@InvokableAction(name = "Resume", description = "Resumes the computer if it was previously paused", alternatives = "unpause;unfreeze;resume", defaultKeyMapping = {"meta+shift+pause","alt+shift+pause"})
|
||||
public void resume() {
|
||||
doResume();
|
||||
}
|
||||
|
@ -18,8 +18,9 @@
|
||||
*/
|
||||
package jace.core;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
/**
|
||||
@ -33,45 +34,54 @@ import javafx.scene.input.KeyEvent;
|
||||
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
|
||||
*/
|
||||
public abstract class KeyHandler {
|
||||
public KeyCombination keyCodeCombination;
|
||||
|
||||
public static enum Modifiers {
|
||||
|
||||
alt, control, shift, meta, shortcut,ignore
|
||||
};
|
||||
public KeyCode key;
|
||||
public EnumMap<Modifiers, Boolean> modifiers;
|
||||
|
||||
public KeyHandler(KeyCode key, Modifiers... flags) {
|
||||
this.key = key;
|
||||
this.modifiers = new EnumMap<>(Modifiers.class);
|
||||
for (Modifiers flag : flags) {
|
||||
modifiers.put(flag, true);
|
||||
public KeyHandler(String comboText) {
|
||||
KeyCode testCode = KeyCode.getKeyCode(comboText);
|
||||
if (testCode != null) {
|
||||
key = testCode;
|
||||
} else {
|
||||
init((KeyCodeCombination) KeyCodeCombination.valueOf(comboText));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matchesModifiers(KeyEvent e) {
|
||||
if (modifiers.get(Modifiers.ignore)) {
|
||||
return true;
|
||||
|
||||
public KeyHandler(KeyCodeCombination keyCodeCombo) {
|
||||
init(keyCodeCombo);
|
||||
}
|
||||
|
||||
private void init(KeyCombination keyCodeCombo) {
|
||||
keyCodeCombination = keyCodeCombo;
|
||||
if (keyCodeCombo instanceof KeyCodeCombination) {
|
||||
key = ((KeyCodeCombination) keyCodeCombo).getCode();
|
||||
}
|
||||
if (e.isAltDown() != modifiers.get(Modifiers.alt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean match(KeyEvent e) {
|
||||
if (keyCodeCombination != null) {
|
||||
return keyCodeCombination.match(e);
|
||||
} else {
|
||||
return e.getCode().equals(key);
|
||||
}
|
||||
if (e.isControlDown() != modifiers.get(Modifiers.control)) {
|
||||
return false;
|
||||
}
|
||||
if (e.isMetaDown() != modifiers.get(Modifiers.meta)) {
|
||||
return false;
|
||||
}
|
||||
if (e.isShiftDown() != modifiers.get(Modifiers.shift)) {
|
||||
return false;
|
||||
}
|
||||
if (e.isShortcutDown() != modifiers.get(Modifiers.shortcut)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract boolean handleKeyUp(KeyEvent e);
|
||||
|
||||
public abstract boolean handleKeyDown(KeyEvent e);
|
||||
|
||||
public String getComboName() {
|
||||
if (keyCodeCombination != null) {
|
||||
return keyCodeCombination.getName();
|
||||
} else {
|
||||
return key.getName();
|
||||
}
|
||||
}
|
||||
|
||||
public String getKeyName() {
|
||||
if (key != null) {
|
||||
return key.getName();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import jace.EmulatorUILogic;
|
||||
import jace.apple2e.SoftSwitches;
|
||||
import jace.apple2e.Speaker;
|
||||
import jace.apple2e.softswitch.KeyboardSoftSwitch;
|
||||
import jace.config.InvokableAction;
|
||||
import jace.config.Reconfigurable;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
@ -29,6 +30,8 @@ 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.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -38,6 +41,7 @@ import java.util.logging.Logger;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
|
||||
/**
|
||||
* Keyboard manages all keyboard-related activities. For now, all hotkeys are
|
||||
@ -89,6 +93,51 @@ public class Keyboard implements Reconfigurable {
|
||||
private static Map<KeyCode, Set<KeyHandler>> keyHandlersByKey = new HashMap<>();
|
||||
private static Map<Object, Set<KeyHandler>> keyHandlersByOwner = new HashMap<>();
|
||||
|
||||
public static void registerInvokableAction(InvokableAction action, Object owner, Method method, String code) {
|
||||
registerKeyHandler(new KeyHandler(code) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
if (!action.notifyOnRelease()) {
|
||||
return false;
|
||||
}
|
||||
System.out.println("Key up: "+method.toString());
|
||||
Object returnValue = null;
|
||||
try {
|
||||
if (method.getParameterCount() > 0) {
|
||||
returnValue = method.invoke(owner, false);
|
||||
} else {
|
||||
returnValue = method.invoke(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());
|
||||
Object returnValue = null;
|
||||
try {
|
||||
if (method.getParameterCount() > 0) {
|
||||
returnValue = method.invoke(owner, true);
|
||||
} else {
|
||||
returnValue = method.invoke(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();
|
||||
}
|
||||
}, owner);
|
||||
}
|
||||
|
||||
public static void registerKeyHandler(KeyHandler l, Object owner) {
|
||||
if (!keyHandlersByKey.containsKey(l.key)) {
|
||||
keyHandlersByKey.put(l.key, new HashSet<>());
|
||||
@ -98,6 +147,7 @@ public class Keyboard implements Reconfigurable {
|
||||
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) {
|
||||
@ -107,12 +157,13 @@ public class Keyboard implements Reconfigurable {
|
||||
keyHandlersByOwner.get(owner).stream().filter((handler) -> !(!keyHandlersByKey.containsKey(handler.key))).forEach((handler) -> {
|
||||
keyHandlersByKey.get(handler.key).remove(handler);
|
||||
});
|
||||
keyHandlersByOwner.remove(owner);
|
||||
}
|
||||
|
||||
public static void processKeyDownEvents(KeyEvent e) {
|
||||
if (keyHandlersByKey.containsKey(e.getCode())) {
|
||||
for (KeyHandler h : keyHandlersByKey.get(e.getCode())) {
|
||||
if (!h.matchesModifiers(e)) {
|
||||
if (!h.match(e)) {
|
||||
continue;
|
||||
}
|
||||
boolean isHandled = h.handleKeyDown(e);
|
||||
@ -127,7 +178,7 @@ public class Keyboard implements Reconfigurable {
|
||||
public static void processKeyUpEvents(KeyEvent e) {
|
||||
if (keyHandlersByKey.containsKey(e.getCode())) {
|
||||
for (KeyHandler h : keyHandlersByKey.get(e.getCode())) {
|
||||
if (!h.matchesModifiers(e)) {
|
||||
if (!h.match(e)) {
|
||||
continue;
|
||||
}
|
||||
boolean isHandled = h.handleKeyUp(e);
|
||||
@ -155,11 +206,11 @@ public class Keyboard implements Reconfigurable {
|
||||
return;
|
||||
}
|
||||
|
||||
char c=255;
|
||||
char c = 255;
|
||||
if (e.getText().length() > 0) {
|
||||
c = e.getText().charAt(0);
|
||||
}
|
||||
|
||||
|
||||
switch (e.getCode()) {
|
||||
case LEFT:
|
||||
case KP_LEFT:
|
||||
@ -189,21 +240,12 @@ public class Keyboard implements Reconfigurable {
|
||||
case BACK_SPACE:
|
||||
c = 127;
|
||||
break;
|
||||
case ALT:
|
||||
pressOpenApple();
|
||||
break;
|
||||
case META:
|
||||
case COMMAND:
|
||||
pressSolidApple();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
if (c < 128) {
|
||||
pressKey((byte) c);
|
||||
}
|
||||
|
||||
// e.consume();
|
||||
}
|
||||
|
||||
public void keyReleased(KeyEvent e) {
|
||||
@ -212,97 +254,21 @@ public class Keyboard implements Reconfigurable {
|
||||
if (code == null || e.isConsumed()) {
|
||||
return;
|
||||
}
|
||||
switch (code) {
|
||||
case INSERT:
|
||||
if (e.isShiftDown()) {
|
||||
doPaste();
|
||||
|
||||
}
|
||||
break;
|
||||
case F10:
|
||||
EmulatorUILogic.toggleDebugPanel();
|
||||
break;
|
||||
case F12:
|
||||
case PAGE_UP:
|
||||
case BACK_SPACE:
|
||||
case PAUSE:
|
||||
if (e.isControlDown()) {
|
||||
computer.warmStart();
|
||||
}
|
||||
break;
|
||||
case F1:
|
||||
EmulatorUILogic.showMediaManager();
|
||||
break;
|
||||
case F4:
|
||||
EmulatorUILogic.showConfig();
|
||||
break;
|
||||
case F7:
|
||||
Speaker.toggleFileOutput();
|
||||
break;
|
||||
case F8:
|
||||
EmulatorUILogic.scaleIntegerRatio();
|
||||
break;
|
||||
case F9:
|
||||
EmulatorUILogic.toggleFullscreen();
|
||||
break;
|
||||
case PRINTSCREEN:
|
||||
case SCROLL_LOCK:
|
||||
try {
|
||||
if (e.isShiftDown()) {
|
||||
EmulatorUILogic.saveScreenshotRaw();
|
||||
} else {
|
||||
EmulatorUILogic.saveScreenshot();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(Keyboard.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
computer.resume();
|
||||
break;
|
||||
case ALT:
|
||||
releaseOpenApple();
|
||||
break;
|
||||
case META:
|
||||
case COMMAND:
|
||||
releaseSolidApple();
|
||||
break;
|
||||
|
||||
}
|
||||
// if ((e.getModifiers() & (KeyEvent.ALT_MASK | KeyEvent.META_MASK | KeyEvent.META_DOWN_MASK)) > 0) {
|
||||
// // explicit left and right here because other locations
|
||||
// // can be sent as well, e.g. KEY_LOCATION_STANDARD
|
||||
// if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT) {
|
||||
// releaseOpenApple();
|
||||
// } else if (e.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT) {
|
||||
// releaseSolidApple();
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
e.consume();
|
||||
// e.setKeyChar((char) 0);
|
||||
// e.setKeyCode(0);
|
||||
}
|
||||
|
||||
private void pressOpenApple() {
|
||||
@InvokableAction(name = "Open Apple Key", alternatives = "OA", category = "Keyboard", notifyOnRelease = true, defaultKeyMapping = "Alt", consumeKeyEvent = false)
|
||||
public void openApple(boolean pressed) {
|
||||
computer.pause();
|
||||
SoftSwitches.PB0.getSwitch().setState(true);
|
||||
SoftSwitches.PB0.getSwitch().setState(pressed);
|
||||
computer.resume();
|
||||
}
|
||||
|
||||
private void pressSolidApple() {
|
||||
@InvokableAction(name = "Closed Apple Key", alternatives = "CA", category = "Keyboard", notifyOnRelease = true, defaultKeyMapping = "Meta", consumeKeyEvent = false)
|
||||
public void solidApple(boolean pressed) {
|
||||
computer.pause();
|
||||
SoftSwitches.PB1.getSwitch().setState(true);
|
||||
computer.resume();
|
||||
}
|
||||
|
||||
private void releaseOpenApple() {
|
||||
computer.pause();
|
||||
SoftSwitches.PB0.getSwitch().setState(false);
|
||||
computer.resume();
|
||||
}
|
||||
|
||||
private void releaseSolidApple() {
|
||||
computer.pause();
|
||||
SoftSwitches.PB1.getSwitch().setState(false);
|
||||
SoftSwitches.PB1.getSwitch().setState(pressed);
|
||||
computer.resume();
|
||||
}
|
||||
|
||||
@ -310,7 +276,8 @@ public class Keyboard implements Reconfigurable {
|
||||
pasteBuffer = new StringReader(text);
|
||||
}
|
||||
|
||||
private static void doPaste() {
|
||||
@InvokableAction(name = "Paste clipboard", alternatives = "paste", category = "Keyboard", notifyOnRelease = false, defaultKeyMapping = {"Ctrl+Shift+V","Shift+Insert"}, consumeKeyEvent = false)
|
||||
public static void doPaste() {
|
||||
try {
|
||||
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
String contents = (String) clip.getData(DataFlavor.stringFlavor);
|
||||
@ -340,8 +307,6 @@ public class Keyboard implements Reconfigurable {
|
||||
return -1;
|
||||
}
|
||||
|
||||
KeyboardSoftSwitch key
|
||||
= (KeyboardSoftSwitch) SoftSwitches.KEYBOARD.getSwitch();
|
||||
return (keypress & 0x0ff);
|
||||
|
||||
} catch (IOException ex) {
|
||||
|
@ -19,7 +19,6 @@
|
||||
package jace.core;
|
||||
|
||||
import jace.state.Stateful;
|
||||
import jace.Emulator;
|
||||
import jace.config.ConfigurableField;
|
||||
import jace.config.InvokableAction;
|
||||
import javafx.scene.image.Image;
|
||||
@ -274,7 +273,8 @@ public abstract class Video extends Device {
|
||||
@InvokableAction(name = "Refresh screen",
|
||||
category = "display",
|
||||
description = "Marks screen contents as changed, forcing full screen redraw",
|
||||
alternatives = "redraw")
|
||||
alternatives = "redraw",
|
||||
defaultKeyMapping = "ctrl+shift+r")
|
||||
public final void forceRefresh() {
|
||||
lineDirty = true;
|
||||
screenDirty = true;
|
||||
|
@ -21,9 +21,9 @@ package jace.hardware;
|
||||
import jace.apple2e.SoftSwitches;
|
||||
import jace.apple2e.softswitch.MemorySoftSwitch;
|
||||
import jace.config.ConfigurableField;
|
||||
import jace.config.InvokableAction;
|
||||
import jace.core.Computer;
|
||||
import jace.core.Device;
|
||||
import jace.core.KeyHandler;
|
||||
import jace.core.Keyboard;
|
||||
import jace.core.RAMEvent;
|
||||
import jace.core.RAMListener;
|
||||
@ -36,8 +36,6 @@ import java.awt.Robot;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
|
||||
/**
|
||||
* Simple implementation of joystick support that supports mouse or keyboard.
|
||||
@ -216,65 +214,31 @@ public class Joystick extends Device {
|
||||
}
|
||||
};
|
||||
|
||||
@InvokableAction(name="Left", category = "joystick", defaultKeyMapping = "left", notifyOnRelease = true)
|
||||
public boolean joystickLeft(boolean pressed) {
|
||||
leftPressed = pressed;
|
||||
return hogKeyboard;
|
||||
};
|
||||
@InvokableAction(name="Right", category = "joystick", defaultKeyMapping = "right", notifyOnRelease = true)
|
||||
public boolean joystickRight(boolean pressed) {
|
||||
rightPressed = pressed;
|
||||
return hogKeyboard;
|
||||
};
|
||||
@InvokableAction(name="Up", category = "joystick", defaultKeyMapping = "up", notifyOnRelease = true)
|
||||
public boolean joystickUp(boolean pressed) {
|
||||
upPressed = pressed;
|
||||
return hogKeyboard;
|
||||
};
|
||||
@InvokableAction(name="Down", category = "joystick", defaultKeyMapping = "down", notifyOnRelease = true)
|
||||
public boolean joystickDown(boolean pressed) {
|
||||
leftPressed = pressed;
|
||||
return hogKeyboard;
|
||||
};
|
||||
|
||||
private void registerListeners() {
|
||||
computer.getMemory().addListener(listener);
|
||||
if (useKeyboard) {
|
||||
System.out.println("Registering key handlers");
|
||||
Keyboard.registerKeyHandler(new KeyHandler(KeyCode.LEFT, KeyHandler.Modifiers.ignore) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
leftPressed = false;
|
||||
return hogKeyboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
leftPressed = true;
|
||||
return hogKeyboard;
|
||||
}
|
||||
}, this);
|
||||
Keyboard.registerKeyHandler(new KeyHandler(KeyCode.RIGHT, KeyHandler.Modifiers.ignore) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
rightPressed = false;
|
||||
return hogKeyboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
rightPressed = true;
|
||||
return hogKeyboard;
|
||||
}
|
||||
}, this);
|
||||
Keyboard.registerKeyHandler(new KeyHandler(KeyCode.UP, KeyHandler.Modifiers.ignore) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
upPressed = false;
|
||||
return hogKeyboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
upPressed = true;
|
||||
return hogKeyboard;
|
||||
}
|
||||
}, this);
|
||||
Keyboard.registerKeyHandler(new KeyHandler(KeyCode.DOWN, KeyHandler.Modifiers.ignore) {
|
||||
@Override
|
||||
public boolean handleKeyUp(KeyEvent e) {
|
||||
downPressed = false;
|
||||
return hogKeyboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyDown(KeyEvent e) {
|
||||
downPressed = true;
|
||||
return hogKeyboard;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void removeListeners() {
|
||||
computer.getMemory().removeListener(listener);
|
||||
Keyboard.unregisterAllHandlers(this);
|
||||
|
Loading…
Reference in New Issue
Block a user