Mouse rerecording
[jpcrr.git] / org / jpc / plugins / VirtualKeyboard.java
blobad1b149b947c6c79b5bfd06cf557960f03f27450
1 /*
2 JPC-RR: A x86 PC Hardware Emulator
3 Release 1
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org.jpc.plugins;
32 import org.jpc.emulator.peripheral.Keyboard;
33 import org.jpc.emulator.KeyboardStatusListener;
34 import org.jpc.pluginsbase.Plugins;
35 import org.jpc.pluginsbase.Plugin;
36 import org.jpc.pluginsaux.ConstantTableLayout;
37 import static org.jpc.Misc.errorDialog;
38 import static org.jpc.Misc.moveWindow;
40 import javax.swing.*;
41 import java.util.*;
42 import java.awt.event.*;
43 import java.awt.*;
45 public class VirtualKeyboard implements ActionListener, Plugin, KeyboardStatusListener
47 private JFrame window;
48 private JPanel panel;
49 private HashMap<String, Integer> commandToKey;
50 private HashMap<String, JToggleButton> commandToButton;
51 private JToggleButton capsLock;
52 private JToggleButton numLock;
53 private JToggleButton scrollLock;
54 private org.jpc.emulator.peripheral.Keyboard keyboard;
55 private int keyNo;
56 private boolean[] cachedState;
57 private Plugins pluginManager;
58 private int nativeWidth, nativeHeight;
60 public void addKey(String name, int scanCode, int x, int y, int w, int h)
62 String cmdName = name + "-" + (keyNo++);
63 JToggleButton button = new JToggleButton(name, false);
64 commandToKey.put(cmdName, new Integer(scanCode));
65 commandToButton.put(cmdName, button);
66 ConstantTableLayout.Placement c = new ConstantTableLayout.Placement(x, y, w, h);
67 panel.add(button, c);
68 button.setActionCommand(cmdName);
69 button.addActionListener(this);
72 public JToggleButton addSpecial(String name, String text, int x, int y, int w, int h)
74 ConstantTableLayout.Placement c = new ConstantTableLayout.Placement(x, y, w, h);
75 JToggleButton button = new JToggleButton(text, false);
76 panel.add(button, c);
77 button.setEnabled(false);
78 button.setVisible(false);
79 return button;
82 public void eci_virtualkeyboard_setwinpos(Integer x, Integer y)
84 moveWindow(window, x.intValue(), y.intValue(), nativeWidth, nativeHeight);
88 public VirtualKeyboard(Plugins _pluginManager)
90 pluginManager = _pluginManager;
91 keyNo = 0;
92 keyboard = null;
93 commandToKey = new HashMap<String, Integer>();
94 commandToButton = new HashMap<String, JToggleButton>();
95 window = new JFrame("Virtual Keyboard");
96 ConstantTableLayout layout = new ConstantTableLayout();
97 cachedState = new boolean[256];
98 panel = new JPanel(layout);
99 window.add(panel);
100 addKey("Esc", 1, 0, 0, 3, 2); //Hack: W should be 2, but we use 3 to make keyboard narrower.
101 addKey("F1", 59, 4, 0, 2, 2);
102 addKey("F2", 60, 6, 0, 2, 2);
103 addKey("F3", 61, 8, 0, 2, 2);
104 addKey("F4", 62, 10, 0, 2, 2);
105 addKey("F5", 63, 13, 0, 2, 2);
106 addKey("F6", 64, 15, 0, 2, 2);
107 addKey("F7", 65, 17, 0, 2, 2);
108 addKey("F8", 66, 19, 0, 2, 2);
109 addKey("F9", 67, 22, 0, 2, 2);
110 addKey("FA", 68, 24, 0, 2, 2);
111 addKey("FB", 87, 26, 0, 2, 2);
112 addKey("FC", 88, 28, 0, 2, 2);
113 addKey("PS", 128 + 55, 31, 0, 2, 2);
114 addKey("SL", 70, 33, 0, 2, 2);
115 addKey("PA", 255, 35, 0, 2, 2);
117 numLock = addSpecial("NumLock", "N", 38, 0, 2, 2);
118 capsLock = addSpecial("CapsLock", "C", 40, 0, 2, 2);
119 scrollLock = addSpecial("ScrollLock", "S", 42, 0, 2, 2);
121 addKey("1", 2, 2, 4, 2, 2);
122 addKey("2", 3, 4, 4, 2, 2);
123 addKey("3", 4, 6, 4, 2, 2);
124 addKey("4", 5, 8, 4, 2, 2);
125 addKey("5", 6, 10, 4, 2, 2);
126 addKey("6", 7, 12, 4, 2, 2);
127 addKey("7", 8, 14, 4, 2, 2);
128 addKey("8", 9, 16, 4, 2, 2);
129 addKey("9", 10, 18, 4, 2, 2);
130 addKey("0", 11, 20, 4, 2, 2);
131 addKey("-", 12, 22, 4, 2, 2);
132 addKey("=", 13, 24, 4, 2, 2);
133 addKey("BS", 14, 26, 4, 4, 2);
134 addKey("I", 128 + 82, 31, 4, 2, 2);
135 addKey("H", 128 + 71, 33, 4, 2, 2);
136 addKey("PU", 128 + 73, 35, 4, 2, 2);
137 addKey("NL", 69, 38, 4, 2, 2);
138 addKey("/", 128 + 53, 40, 4, 2, 2);
139 addKey("*", 55, 42, 4, 2, 2);
140 addKey("-", 74, 44, 4, 2, 2);
142 addKey("Tab", 15, 0, 6, 3, 2);
143 addKey("Q", 16, 3, 6, 2, 2);
144 addKey("W", 17, 5, 6, 2, 2);
145 addKey("E", 18, 7, 6, 2, 2);
146 addKey("R", 19, 9, 6, 2, 2);
147 addKey("T", 20, 11, 6, 2, 2);
148 addKey("Y", 21, 13, 6, 2, 2);
149 addKey("U", 22, 15, 6, 2, 2);
150 addKey("I", 23, 17, 6, 2, 2);
151 addKey("O", 24, 19, 6, 2, 2);
152 addKey("P", 25, 21, 6, 2, 2);
153 addKey("[", 26, 23, 6, 2, 2);
154 addKey("]", 27, 25, 6, 2, 2);
155 addKey("EN", 28, 28, 6, 2, 4);
156 addKey("D", 128 + 83, 31, 6, 2, 2);
157 addKey("E", 128 + 79, 33, 6, 2, 2);
158 addKey("PD", 128 + 81, 35, 6, 2, 2);
159 addKey("7", 71, 38, 6, 2, 2);
160 addKey("8", 72, 40, 6, 2, 2);
161 addKey("9", 73, 42, 6, 2, 2);
162 addKey("+", 78, 44, 6, 2, 4);
164 addKey("CL", 58, 0, 8, 4, 2);
165 addKey("A", 30, 4, 8, 2, 2);
166 addKey("S", 31, 6, 8, 2, 2);
167 addKey("D", 32, 8, 8, 2, 2);
168 addKey("F", 33, 10, 8, 2, 2);
169 addKey("G", 34, 12, 8, 2, 2);
170 addKey("H", 35, 14, 8, 2, 2);
171 addKey("J", 36, 16, 8, 2, 2);
172 addKey("K", 37, 18, 8, 2, 2);
173 addKey("L", 38, 20, 8, 2, 2);
174 addKey(";", 39, 22, 8, 2, 2);
175 addKey("'", 40, 24, 8, 2, 2);
176 addKey("`", 41, 26, 8, 2, 2);
177 addKey("4", 75, 38, 8, 2, 2);
178 addKey("5", 76, 40, 8, 2, 2);
179 addKey("6", 77, 42, 8, 2, 2);
181 addKey("SH", 42, 0, 10, 3, 2);
182 addKey("\\", 43, 3, 10, 2, 2);
183 addKey("Z", 44, 5, 10, 2, 2);
184 addKey("X", 45, 7, 10, 2, 2);
185 addKey("C", 46, 9, 10, 2, 2);
186 addKey("V", 47, 11, 10, 2, 2);
187 addKey("B", 48, 13, 10, 2, 2);
188 addKey("N", 49, 15, 10, 2, 2);
189 addKey("M", 50, 17, 10, 2, 2);
190 addKey(",", 51, 19, 10, 2, 2);
191 addKey(".", 52, 21, 10, 2, 2);
192 addKey("/", 53, 23, 10, 2, 2);
193 addKey("SH", 54, 25, 10, 5, 2);
194 addKey("^", 128 + 72, 33, 10, 2, 2);
195 addKey("1", 79, 38, 10, 2, 2);
196 addKey("2", 80, 40, 10, 2, 2);
197 addKey("3", 81, 42, 10, 2, 2);
198 addKey("EN", 128 + 28, 44, 10, 2, 4);
200 addKey("CT", 29, 0, 12, 3, 2);
201 addKey("AL", 56, 5, 12, 3, 2);
202 addKey("SP", 57, 8, 12, 14, 2);
203 addKey("AL", 128 + 56, 22, 12, 3, 2);
204 addKey("CT", 128 + 29, 27, 12, 3, 2);
205 addKey("<", 128 + 75, 31, 12, 2, 2);
206 addKey("v", 128 + 80, 33, 12, 2, 2 );
207 addKey(">", 128 + 77, 35, 12, 2, 2 );
208 addKey("0", 82, 38, 12, 4, 2);
209 addKey(".", 83, 42, 12, 2, 2);
211 window.pack();
212 window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
213 Dimension d = window.getSize();
214 nativeWidth = d.width;
215 nativeHeight = d.height;
216 window.setVisible(true);
219 //-1 if unknonw, bit 2 is capslock, bit 1 is numlock, bit 0 is scrollock.
220 private void updateLEDs(int status)
222 if(status < 0) {
223 numLock.setVisible(false);
224 numLock.setSelected(false);
225 capsLock.setVisible(false);
226 capsLock.setSelected(false);
227 scrollLock.setVisible(false);
228 scrollLock.setSelected(false);
229 } else {
230 numLock.setVisible(true);
231 capsLock.setVisible(true);
232 scrollLock.setVisible(true);
233 numLock.setSelected((status & 2) != 0);
234 capsLock.setSelected((status & 4) != 0);
235 scrollLock.setSelected((status & 1) != 0);
239 public void resetButtons()
241 for(Map.Entry<String, Integer> entry : commandToKey.entrySet()) {
242 int scan = entry.getValue().intValue();
243 JToggleButton button = commandToButton.get(entry.getKey());
244 if(keyboard.getKeyStatus((byte)scan) != cachedState[scan]) {
245 cachedState[scan] = keyboard.getKeyStatus((byte)scan);
246 button.setSelected(cachedState[scan]);
249 updateLEDs(keyboard.getLEDStatus());
252 private void keyStatusChangeEventThread(int scancode, boolean pressed)
254 /* THIS IS JUST PLAIN BROKEN.
255 for(Map.Entry<String, Integer> entry : commandToKey.entrySet()) {
256 int scan = entry.getValue().intValue();
257 if(scan != scancode)
258 continue;
259 JToggleButton button = commandToButton.get(entry.getKey());
260 if(pressed != cachedState[scan]) {
261 cachedState[scan] = pressed;
262 button.setSelected(pressed);
268 public void keyExecStatusChange(int scancode, boolean pressed)
270 //These aren't currently shown.
273 public void keyStatusChange(int scancode, boolean pressed)
275 if(!SwingUtilities.isEventDispatchThread())
276 try {
277 final int _scancode = scancode;
278 final boolean _pressed = pressed;
279 SwingUtilities.invokeLater(new Thread() { public void run() { VirtualKeyboard.this.keyStatusChangeEventThread(_scancode, _pressed); }});
280 } catch(Exception e) {
282 else
283 keyStatusChangeEventThread(scancode, pressed);
286 public void keyStatusReload()
288 if(!SwingUtilities.isEventDispatchThread())
289 try {
290 SwingUtilities.invokeLater(new Thread() { public void run() { VirtualKeyboard.this.resetButtons(); }});
291 } catch(Exception e) {
293 else
294 resetButtons();
297 public void ledStatusChange(int newstatus)
299 if(!SwingUtilities.isEventDispatchThread())
300 try {
301 final int _newstatus = newstatus;
302 SwingUtilities.invokeLater(new Thread() { public void run() { VirtualKeyboard.this.updateLEDs(_newstatus); }});
303 } catch(Exception e) {
305 else
306 updateLEDs(newstatus);
309 public void mouseButtonsChange(int newstatus)
311 //Not interesting.
314 public void mouseExecButtonsChange(int newstatus)
316 //Not interesting.
319 public void main()
321 //This runs entierely in UI thread.
324 public boolean systemShutdown()
326 //OK to proceed with JVM shutdown.
327 return true;
330 public void pcStarting()
332 //Not interested.
335 public void pcStopping()
337 if(pluginManager.isShuttingDown())
338 return; //Too much of deadlock risk.
340 if(!SwingUtilities.isEventDispatchThread())
341 try {
342 SwingUtilities.invokeAndWait(new Thread() { public void run() { VirtualKeyboard.this.resetButtons(); }});
343 } catch(Exception e) {
345 else
346 resetButtons();
349 public void reconnect(org.jpc.emulator.PC pc)
351 if(keyboard != null)
352 keyboard.removeStatusListener(this);
353 if(pc != null) {
354 Keyboard keys = (Keyboard)pc.getComponent(Keyboard.class);
355 keyboard = keys;
356 keyboard.addStatusListener(this);
357 keyStatusReload();
358 } else {
359 keyboard = null;
360 Iterator<Map.Entry<String, Integer> > itt = commandToKey.entrySet().iterator();
361 while (itt.hasNext())
363 Map.Entry<String, Integer> entry = itt.next();
364 String n = entry.getKey();
365 Integer s = entry.getValue();
366 cachedState[s.intValue()] = false;
367 commandToButton.get(n).setSelected(false);
368 ledStatusChange(-1);
373 public void actionPerformed(ActionEvent evt)
375 if(keyboard == null)
376 return;
378 String command = evt.getActionCommand();
379 JToggleButton button = commandToButton.get(command);
380 int scan = commandToKey.get(command).intValue();
381 if(button.isSelected())
382 System.err.println("Informational: Keydown on key " + scan + ".");
383 else
384 System.err.println("Informational: Keyup on key " + scan + ".");
385 try {
386 keyboard.sendEdge(scan);
387 if(scan != 255 && (evt.getModifiers() & ActionEvent.SHIFT_MASK) != 0)
388 keyboard.sendEdge(scan);
389 } catch(Exception e) {
390 System.err.println("Error: Sending command failed: " + e);
391 errorDialog(e, "Failed to send keyboard key edge", null, "Dismiss");
393 if(scan != 255 && (evt.getModifiers() & ActionEvent.SHIFT_MASK) == 0)
394 cachedState[scan] = !cachedState[scan];
395 button.setSelected(cachedState[scan]);