From a477abac038e1c879682f7f03bdc02b65aae8718 Mon Sep 17 00:00:00 2001 From: Brendan Robert Date: Wed, 18 Feb 2015 08:41:59 -0600 Subject: [PATCH] Completely reworked how hotkeys are handled, and now invokable actions are a real concept in the emulator! --- src/main/java/jace/Emulator.java | 1 + src/main/java/jace/EmulatorUILogic.java | 41 ++++- src/main/java/jace/cheat/MetaCheats.java | 30 +--- src/main/java/jace/config/Configuration.java | 54 +++++- .../java/jace/config/InvokableAction.java | 15 ++ src/main/java/jace/core/Computer.java | 17 +- src/main/java/jace/core/KeyHandler.java | 72 ++++---- src/main/java/jace/core/Keyboard.java | 163 +++++++----------- src/main/java/jace/core/Video.java | 4 +- src/main/java/jace/hardware/Joystick.java | 82 +++------ 10 files changed, 242 insertions(+), 237 deletions(-) diff --git a/src/main/java/jace/Emulator.java b/src/main/java/jace/Emulator.java index 69853f7..b23292c 100644 --- a/src/main/java/jace/Emulator.java +++ b/src/main/java/jace/Emulator.java @@ -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) { diff --git a/src/main/java/jace/EmulatorUILogic.java b/src/main/java/jace/EmulatorUILogic.java index be1fc13..fb68174 100644 --- a/src/main/java/jace/EmulatorUILogic.java +++ b/src/main/java/jace/EmulatorUILogic.java @@ -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() { + } +} \ No newline at end of file diff --git a/src/main/java/jace/cheat/MetaCheats.java b/src/main/java/jace/cheat/MetaCheats.java index f64ef88..0798833 100644 --- a/src/main/java/jace/cheat/MetaCheats.java +++ b/src/main/java/jace/cheat/MetaCheats.java @@ -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 diff --git a/src/main/java/jace/config/Configuration.java b/src/main/java/jace/config/Configuration.java index 3898f6d..fd58300 100644 --- a/src/main/java/jace/config/Configuration.java +++ b/src/main/java/jace/config/Configuration.java @@ -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 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 settings; + private Map hotkeys; protected Map 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 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); diff --git a/src/main/java/jace/config/InvokableAction.java b/src/main/java/jace/config/InvokableAction.java index d95b4b3..9f96782 100644 --- a/src/main/java/jace/config/InvokableAction.java +++ b/src/main/java/jace/config/InvokableAction.java @@ -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(); } \ No newline at end of file diff --git a/src/main/java/jace/core/Computer.java b/src/main/java/jace/core/Computer.java index 532de05..51763cc 100644 --- a/src/main/java/jace/core/Computer.java +++ b/src/main/java/jace/core/Computer.java @@ -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(); } diff --git a/src/main/java/jace/core/KeyHandler.java b/src/main/java/jace/core/KeyHandler.java index 5bc28df..d466c84 100644 --- a/src/main/java/jace/core/KeyHandler.java +++ b/src/main/java/jace/core/KeyHandler.java @@ -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; - - 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; + } + } } diff --git a/src/main/java/jace/core/Keyboard.java b/src/main/java/jace/core/Keyboard.java index e642988..a9dab15 100644 --- a/src/main/java/jace/core/Keyboard.java +++ b/src/main/java/jace/core/Keyboard.java @@ -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> keyHandlersByKey = new HashMap<>(); private static Map> 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) { diff --git a/src/main/java/jace/core/Video.java b/src/main/java/jace/core/Video.java index 5f0057e..5cdbeb7 100644 --- a/src/main/java/jace/core/Video.java +++ b/src/main/java/jace/core/Video.java @@ -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; diff --git a/src/main/java/jace/hardware/Joystick.java b/src/main/java/jace/hardware/Joystick.java index 5929f98..09000cc 100644 --- a/src/main/java/jace/hardware/Joystick.java +++ b/src/main/java/jace/hardware/Joystick.java @@ -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);