Directly using VirtualBox from Java #5
7 changed files with 270 additions and 63 deletions
|
@ -70,7 +70,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spigotmc</groupId>
|
<groupId>org.spigotmc</groupId>
|
||||||
<artifactId>spigot-api</artifactId>
|
<artifactId>spigot-api</artifactId>
|
||||||
<version>1.9.2-R0.1-SNAPSHOT</version>
|
<version>1.12-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
|
@ -89,10 +89,6 @@
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
<exclusion>
|
|
||||||
<artifactId>ebean</artifactId>
|
|
||||||
<groupId>org.avaje</groupId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
|
@ -109,6 +105,12 @@
|
||||||
<version>3.0.0</version>
|
<version>3.0.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bukkit</groupId>
|
||||||
|
<artifactId>craftbukkit</artifactId>
|
||||||
|
<version>1.12-R0.1-SNAPSHOT</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,38 @@
|
||||||
package sznp.virtualcomputer;
|
package sznp.virtualcomputer;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.DataBufferInt;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.map.MapCanvas;
|
import org.bukkit.map.MapCanvas;
|
||||||
import org.bukkit.map.MapRenderer;
|
import org.bukkit.map.MapRenderer;
|
||||||
import org.bukkit.map.MapView;
|
import org.bukkit.map.MapView;
|
||||||
|
|
||||||
public class BukkitRenderer extends MapRenderer implements IRenderer {
|
public class BukkitRenderer extends MapRenderer implements IRenderer {
|
||||||
|
private byte[] allpixels;
|
||||||
private BufferedImage image;
|
private BufferedImage image;
|
||||||
|
private int startindex;
|
||||||
|
|
||||||
public BukkitRenderer(BufferedImage image) {
|
/**
|
||||||
this.image = image;
|
* Generic implementation, should work on most versions
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* The ID of the current map
|
||||||
|
* @param world
|
||||||
|
* The world to create new maps in
|
||||||
|
* @param allpixels
|
||||||
|
* The raw pixel data from the machine in BGRA format
|
||||||
|
* @param startindex
|
||||||
|
* The index to start from in allpixels
|
||||||
|
*/
|
||||||
|
public BukkitRenderer(short id, World world, byte[] allpixels, int startindex) {
|
||||||
|
MapView map = IRenderer.prepare(id, world);
|
||||||
|
map.addRenderer(this);
|
||||||
|
this.allpixels = allpixels;
|
||||||
|
this.startindex = startindex;
|
||||||
|
image = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int progress = 0;
|
private int progress = 0;
|
||||||
|
@ -22,6 +42,19 @@ public class BukkitRenderer extends MapRenderer implements IRenderer {
|
||||||
public void render(MapView view, MapCanvas canvas, Player player) {
|
public void render(MapView view, MapCanvas canvas, Player player) {
|
||||||
long time = System.nanoTime();
|
long time = System.nanoTime();
|
||||||
|
|
||||||
|
final int[] a = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); // Directly update the bytes of the image
|
||||||
|
|
||||||
|
// (byte) bgra to rgb (int)
|
||||||
|
for (int i = startindex, j = 0; i < startindex + 128 * 128; i = i + 4, j++) {
|
||||||
|
int b, g, r;
|
||||||
|
|
||||||
|
b = allpixels[i] & 0xFF;
|
||||||
|
g = allpixels[i + 1] & 0xFF;
|
||||||
|
r = allpixels[i + 2] & 0xFF;
|
||||||
|
|
||||||
|
a[j] = (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
canvas.drawImage(0, progress * updatepixels, image.getSubimage(0, progress * updatepixels, 128,
|
canvas.drawImage(0, progress * updatepixels, image.getSubimage(0, progress * updatepixels, 128,
|
||||||
(progress * updatepixels + updatepixels >= 128 ? 128 - progress * updatepixels : updatepixels)));
|
(progress * updatepixels + updatepixels >= 128 ? 128 - progress * updatepixels : updatepixels)));
|
||||||
|
|
|
@ -62,7 +62,7 @@ public class Commands implements CommandExecutor
|
||||||
case "key":
|
case "key":
|
||||||
if (args.length < 2)
|
if (args.length < 2)
|
||||||
{
|
{
|
||||||
sender.sendMessage("§cUsage: /computer key <key> [down/up|interval]");
|
sender.sendMessage("§cUsage: /computer key <key> [down/up|duration(ticks)]");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (args.length < 3)
|
if (args.length < 3)
|
||||||
|
@ -187,8 +187,7 @@ public class Commands implements CommandExecutor
|
||||||
}
|
}
|
||||||
MouseLockerPlayerListener.LockedSpeed = Float
|
MouseLockerPlayerListener.LockedSpeed = Float
|
||||||
.parseFloat(args[2]);
|
.parseFloat(args[2]);
|
||||||
sender.sendMessage("§aMouse speed set to "
|
sender.sendMessage("§aMouse speed set to " + MouseLockerPlayerListener.LockedSpeed);
|
||||||
+ MouseLockerPlayerListener.LockedSpeed);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,9 @@ public class DirectRenderer implements IRenderer {
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* Usually happens on incompatibility
|
* Usually happens on incompatibility
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public DirectRenderer(short id, World world, byte[] allpixels, int startindex)
|
public DirectRenderer(short id, World world, byte[] allpixels, int startindex)
|
||||||
throws Exception, Exception, Exception, Exception {
|
throws Exception, Exception, Exception, Exception {
|
||||||
map = Bukkit.getMap(id);
|
map = IRenderer.prepare(id, world);
|
||||||
if (map == null)
|
|
||||||
map = Bukkit.createMap(world);
|
|
||||||
map.getRenderers().clear();
|
|
||||||
final Field field = map.getClass().getDeclaredField("renderCache");
|
final Field field = map.getClass().getDeclaredField("renderCache");
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
|
@ -1,4 +1,16 @@
|
||||||
package sznp.virtualcomputer;
|
package sznp.virtualcomputer;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.map.MapView;
|
||||||
|
|
||||||
public interface IRenderer {
|
public interface IRenderer {
|
||||||
|
static MapView prepare(short id, World world) {
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
MapView map = Bukkit.getMap(id);
|
||||||
|
if (map == null)
|
||||||
|
map = Bukkit.createMap(world);
|
||||||
|
map.getRenderers().clear();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package sznp.virtualcomputer;
|
package sznp.virtualcomputer;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.lang.reflect.Field;
|
||||||
import java.awt.image.DataBufferInt;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import net.countercraft.movecraft.craft.Craft;
|
import net.countercraft.movecraft.craft.Craft;
|
||||||
import net.countercraft.movecraft.craft.CraftManager;
|
import net.countercraft.movecraft.craft.CraftManager;
|
||||||
|
@ -12,21 +12,18 @@ import org.bukkit.Material;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.ConsoleCommandSender;
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.virtualbox_5_1.*;
|
||||||
import org.virtualbox_5_1.IFramebuffer;
|
|
||||||
import org.virtualbox_5_1.ISession;
|
|
||||||
import org.virtualbox_5_1.IVirtualBox;
|
|
||||||
import org.virtualbox_5_1.VirtualBoxManager;
|
|
||||||
|
|
||||||
import com.mcplugindev.slipswhitley.sketchmap.map.RelativeLocation;
|
import com.google.common.collect.Lists;
|
||||||
import com.mcplugindev.slipswhitley.sketchmap.map.SketchMap;
|
|
||||||
|
|
||||||
public class PluginMain extends JavaPlugin {
|
public class PluginMain extends JavaPlugin {
|
||||||
private IVirtualBox vbox;
|
private IVirtualBox vbox;
|
||||||
private ISession session;
|
private ISession session;
|
||||||
private SketchMap smap;
|
private ArrayList<IRenderer> renderers = new ArrayList<>();
|
||||||
|
private IMachine machine;
|
||||||
|
|
||||||
public static PluginMain Instance;
|
public static PluginMain Instance;
|
||||||
|
public static byte[] allpixels = new byte[640 * 480];
|
||||||
|
|
||||||
// Fired when plugin is first enabled
|
// Fired when plugin is first enabled
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,21 +33,27 @@ public class PluginMain extends JavaPlugin {
|
||||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||||
this.getCommand("computer").setExecutor(new Commands());
|
this.getCommand("computer").setExecutor(new Commands());
|
||||||
ccs.sendMessage("§bInitializing VirtualBox...");
|
ccs.sendMessage("§bInitializing VirtualBox...");
|
||||||
|
final String vbpath = System.getProperty("os.name").toLowerCase().contains("mac")
|
||||||
|
? "/Applications/VirtualBox.app/Contents/MacOS" : "/opt/virtualbox";
|
||||||
if (System.getProperty("vbox.home") == null || System.getProperty("vbox.home").isEmpty())
|
if (System.getProperty("vbox.home") == null || System.getProperty("vbox.home").isEmpty())
|
||||||
System.setProperty("vbox.home", "/opt/virtualbox");
|
System.setProperty("vbox.home", vbpath);
|
||||||
|
if (System.getProperty("sun.boot.library.path") == null
|
||||||
|
|| System.getProperty("sun.boot.library.path").isEmpty())
|
||||||
|
System.setProperty("sun.boot.library.path", vbpath);
|
||||||
|
addLibraryPath(vbpath);
|
||||||
final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath());
|
final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath());
|
||||||
vbox = manager.getVBox();
|
vbox = manager.getVBox();
|
||||||
session = manager.getSessionObject();
|
session = manager.getSessionObject();
|
||||||
ccs.sendMessage("§bStarting VM for testing...");
|
ccs.sendMessage("§bLoading Screen...");
|
||||||
vbox.getMachines().get(0).launchVMProcess(session, "headless", "").waitForCompletion(2000);
|
try {
|
||||||
session.getConsole().getDisplay().attachFramebuffer(0L, new IFramebuffer(new MCFrameBuffer()));
|
for (short i = 0; i < 20; i++)
|
||||||
ccs.sendMessage("§bLoading SketchMap...");
|
renderers.add(new DirectRenderer(i, Bukkit.getWorlds().get(0), allpixels, i * 128 * 128 * 4)); // TODO: The pixels are selected in a horribly wrong way probably
|
||||||
img = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
|
ccs.sendMessage("§bUsing Direct Renderer");
|
||||||
HashMap<Short, RelativeLocation> map = new HashMap<>();
|
} catch (NoClassDefFoundError e) {
|
||||||
for (int i = 0; i < 5; i++)
|
for (short i = 0; i < 20; i++)
|
||||||
for (int j = 0; j < 4; j++)
|
renderers.add(new BukkitRenderer(i, Bukkit.getWorlds().get(0), allpixels, i * 128 * 128 * 4));
|
||||||
map.put((short) (i * 4 + j), new RelativeLocation(i, j));
|
ccs.sendMessage("§6Compability error, using slower renderer");
|
||||||
smap = new SketchMap(img, "Screen", 5, 4, false, map);
|
}
|
||||||
ccs.sendMessage("§bLoaded!");
|
ccs.sendMessage("§bLoaded!");
|
||||||
getServer().getPluginManager().registerEvents(new MouseLockerPlayerListener(), this);
|
getServer().getPluginManager().registerEvents(new MouseLockerPlayerListener(), this);
|
||||||
DoStart();
|
DoStart();
|
||||||
|
@ -67,12 +70,12 @@ public class PluginMain extends JavaPlugin {
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
private volatile BufferedImage img;
|
|
||||||
private volatile BukkitTask task = null;
|
|
||||||
|
|
||||||
public void Start(CommandSender sender) {
|
public void Start(CommandSender sender) {
|
||||||
sender.sendMessage("§eStarting computer...");
|
sender.sendMessage("§eStarting computer...");
|
||||||
// computer.Start();
|
if (machine == null)
|
||||||
|
machine = vbox.getMachines().get(0);
|
||||||
|
machine.launchVMProcess(session, "headless", "").waitForCompletion(10000);
|
||||||
|
session.getConsole().getDisplay().attachFramebuffer(0L, new IFramebuffer(new MCFrameBuffer()));
|
||||||
sender.sendMessage("§eComputer started.");
|
sender.sendMessage("§eComputer started.");
|
||||||
DoStart();
|
DoStart();
|
||||||
}
|
}
|
||||||
|
@ -80,14 +83,6 @@ public class PluginMain extends JavaPlugin {
|
||||||
public static int MouseSpeed = 1;
|
public static int MouseSpeed = 1;
|
||||||
|
|
||||||
private void DoStart() {
|
private void DoStart() {
|
||||||
if (task == null)
|
|
||||||
task = this.getServer().getScheduler().runTaskTimerAsynchronously(this, new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
final int[] a = ((DataBufferInt) smap.image.getRaster().getDataBuffer()).getData();
|
|
||||||
// final int[] data = computer.GetScreenPixelColors();
|
|
||||||
// System.arraycopy(data, 0, a, 0, data.length);
|
|
||||||
}
|
|
||||||
}, 1, 10);
|
|
||||||
if (getServer().getPluginManager().isPluginEnabled("Movecraft")) {
|
if (getServer().getPluginManager().isPluginEnabled("Movecraft")) {
|
||||||
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
|
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -113,50 +108,106 @@ public class PluginMain extends JavaPlugin {
|
||||||
|
|
||||||
public void Stop(CommandSender sender) {
|
public void Stop(CommandSender sender) {
|
||||||
sender.sendMessage("§eStopping computer...");
|
sender.sendMessage("§eStopping computer...");
|
||||||
// computer.PowerOff();
|
session.getConsole().powerDown().waitForCompletion(2000);
|
||||||
sender.sendMessage("§eComputer stopped.");
|
sender.sendMessage("§eComputer stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PowerButton(CommandSender sender) {
|
public void PowerButton(CommandSender sender) {
|
||||||
sender.sendMessage("§eStarting/stoppping computer...");
|
sender.sendMessage("§ePressing powerbutton...");
|
||||||
final CommandSender s = sender;
|
final CommandSender s = sender;
|
||||||
getServer().getScheduler().runTaskAsynchronously(this, new Runnable() {
|
getServer().getScheduler().runTaskAsynchronously(this, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
/*
|
if (session.getState() != SessionState.Locked || session.getMachine() == null) {
|
||||||
* if (computer.PowerButton()) { DoStart(); s.sendMessage("§eComputer started."); } else s.sendMessage("§ePowerbutton pressed.");
|
Start(sender);
|
||||||
*/
|
} else {
|
||||||
|
session.getConsole().powerButton();
|
||||||
|
s.sendMessage("§ePowerbutton pressed.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(CommandSender sender) {
|
public void Reset(CommandSender sender) {
|
||||||
sender.sendMessage("§eResetting computer...");
|
sender.sendMessage("§eResetting computer...");
|
||||||
// computer.Reset();
|
if (session.getState() == SessionState.Locked)
|
||||||
|
session.getConsole().powerDown().waitForCompletion(10000);
|
||||||
sender.sendMessage("§eComputer reset.");
|
sender.sendMessage("§eComputer reset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FixScreen(CommandSender sender) {
|
public void FixScreen(CommandSender sender) {
|
||||||
sender.sendMessage("§eFixing screen...");
|
sender.sendMessage("§eFixing screen...");
|
||||||
// computer.FixScreen();
|
session.getConsole().getDisplay().setSeamlessMode(false);
|
||||||
|
session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L);
|
||||||
sender.sendMessage("§eScreen fixed.");
|
sender.sendMessage("§eScreen fixed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PressKey(CommandSender sender, String key, String stateorduration) {
|
public void PressKey(CommandSender sender, String key, String stateorduration) {
|
||||||
/*
|
if (session.getState() == SessionState.Locked) {
|
||||||
* if (stateorduration.length() == 0) computer.PressKey(key, (short) 0); else if (stateorduration.equalsIgnoreCase("down")) computer.PressKey(key, (short) -1); else if
|
int durationorstate;
|
||||||
* (stateorduration.equalsIgnoreCase("up")) computer.PressKey(key, (short) -2); else computer.PressKey(key, Short.parseShort(stateorduration));
|
if (stateorduration.length() == 0)
|
||||||
*/
|
durationorstate = 0;
|
||||||
|
else if (stateorduration.equalsIgnoreCase("down"))
|
||||||
|
durationorstate = -1;
|
||||||
|
else if (stateorduration.equalsIgnoreCase("up"))
|
||||||
|
durationorstate = -2;
|
||||||
|
else
|
||||||
|
durationorstate = Short.parseShort(stateorduration);
|
||||||
|
int code = 0;
|
||||||
|
// Release key scan code concept taken from VirtualBox source code (KeyboardImpl.cpp:putCAD())
|
||||||
|
// +128
|
||||||
|
if (durationorstate != 2)
|
||||||
|
session.getConsole().getKeyboard().putScancode(code);
|
||||||
|
Runnable sendrelease = () -> session.getConsole().getKeyboard()
|
||||||
|
.putScancodes(Lists.newArrayList(code + 128, Scancode.sc_controlLeft.Code + 128,
|
||||||
|
Scancode.sc_shiftLeft.Code + 128, Scancode.sc_altLeft.Code + 128));
|
||||||
|
if (durationorstate == 0 || durationorstate == -2)
|
||||||
|
sendrelease.run();
|
||||||
|
if (durationorstate > 0) {
|
||||||
|
Bukkit.getScheduler().runTaskLaterAsynchronously(this, sendrelease, durationorstate);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs, boolean down) {
|
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs, boolean down) {
|
||||||
/*
|
if (session.getState() != SessionState.Locked)
|
||||||
* if (down) computer.UpdateMouse(x, y, z, w, mbs); else computer.UpdateMouse(x, y, z, w, "");
|
return;
|
||||||
*/
|
int state = 0;
|
||||||
|
if (mbs.length() > 0 && down)
|
||||||
|
state = Arrays.stream(MouseButtonState.values()).filter(mousebs -> mousebs.name().equalsIgnoreCase(mbs))
|
||||||
|
.findAny().orElseThrow(() -> new RuntimeException("Unknown mouse button")).value();
|
||||||
|
session.getConsole().getMouse().putMouseEvent(x, y, z, w, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs) {
|
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs) {
|
||||||
UpdateMouse(sender, x, y, z, w, mbs, true);
|
UpdateMouse(sender, x, y, z, w, mbs, true);
|
||||||
UpdateMouse(sender, x, y, z, w, mbs, false);
|
UpdateMouse(sender, x, y, z, w, mbs, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified path to the java library path
|
||||||
|
*
|
||||||
|
* @param pathToAdd
|
||||||
|
* the path to add
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static void addLibraryPath(String pathToAdd) throws Exception {
|
||||||
|
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
|
||||||
|
usrPathsField.setAccessible(true);
|
||||||
|
|
||||||
|
// get array of paths
|
||||||
|
final String[] paths = (String[]) usrPathsField.get(null);
|
||||||
|
|
||||||
|
// check if the path to add is already present
|
||||||
|
for (String path : paths) {
|
||||||
|
if (path.equals(pathToAdd)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the new path
|
||||||
|
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
|
||||||
|
newPaths[newPaths.length - 1] = pathToAdd;
|
||||||
|
usrPathsField.set(null, newPaths);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
114
VirtualComputer/src/sznp/virtualcomputer/Scancode.java
Normal file
114
VirtualComputer/src/sznp/virtualcomputer/Scancode.java
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package sznp.virtualcomputer;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The scancode values come from:
|
||||||
|
- http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc (March 16), 2000).
|
||||||
|
- http://www.computer-engineering.org/ps2keyboard/scancodes1.html
|
||||||
|
- using MapVirtualKeyEx( VK_*), MAPVK_VK_TO_VSC_EX), 0 ) with the english us keyboard layout
|
||||||
|
- reading win32 WM_INPUT keyboard messages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum Scancode { // https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names
|
||||||
|
|
||||||
|
sc_escape(0x01), sc_1(0x02), sc_2(0x03), sc_3(0x04), sc_4(0x05), sc_5(0x06), sc_6(0x07), sc_7(0x08), sc_8(
|
||||||
|
0x09), sc_9(0x0A), sc_0(0x0B), sc_minus(0x0C), sc_equals(0x0D), sc_backspace(0x0E), sc_tab(0x0F), sc_q(
|
||||||
|
0x10), sc_w(0x11), sc_e(0x12), sc_r(0x13), sc_t(0x14), sc_y(0x15), sc_u(0x16), sc_i(0x17), sc_o(
|
||||||
|
0x18), sc_p(0x19), sc_bracketLeft(0x1A), sc_bracketRight(0x1B), sc_enter(
|
||||||
|
0x1C), sc_controlLeft(0x1D), sc_a(0x1E), sc_s(0x1F), sc_d(
|
||||||
|
0x20), sc_f(0x21), sc_g(0x22), sc_h(0x23), sc_j(0x24), sc_k(0x25), sc_l(
|
||||||
|
0x26), sc_semicolon(0x27), sc_apostrophe(0x28), sc_grave(
|
||||||
|
0x29), sc_shiftLeft(0x2A), sc_backslash(0x2B), sc_z(
|
||||||
|
0x2C), sc_x(0x2D), sc_c(0x2E), sc_v(0x2F), sc_b(
|
||||||
|
0x30), sc_n(0x31), sc_m(0x32), sc_comma(
|
||||||
|
0x33), sc_preiod(0x34), sc_slash(
|
||||||
|
0x35), sc_shiftRight(
|
||||||
|
0x36), sc_numpad_multiply(
|
||||||
|
0x37), sc_altLeft(
|
||||||
|
0x38), sc_space(
|
||||||
|
0x39), sc_capsLock(
|
||||||
|
0x3A), sc_f1(
|
||||||
|
0x3B), sc_f2(
|
||||||
|
0x3C), sc_f3(
|
||||||
|
0x3D), sc_f4(
|
||||||
|
0x3E), sc_f5(
|
||||||
|
0x3F), sc_f6(
|
||||||
|
0x40), sc_f7(
|
||||||
|
0x41), sc_f8(
|
||||||
|
0x42), sc_f9(
|
||||||
|
0x43), sc_f10(
|
||||||
|
0x44), sc_numLock(
|
||||||
|
0x45), sc_scrollLock(
|
||||||
|
0x46), sc_numpad_7(
|
||||||
|
0x47), sc_numpad_8(
|
||||||
|
0x48), sc_numpad_9(
|
||||||
|
0x49), sc_numpad_minus(
|
||||||
|
0x4A), sc_numpad_4(
|
||||||
|
0x4B), sc_numpad_5(
|
||||||
|
0x4C), sc_numpad_6(
|
||||||
|
0x4D), sc_numpad_plus(
|
||||||
|
0x4E), sc_numpad_1(
|
||||||
|
0x4F), sc_numpad_2(
|
||||||
|
0x50), sc_numpad_3(
|
||||||
|
0x51), sc_numpad_0(
|
||||||
|
0x52), sc_numpad_period(
|
||||||
|
0x53), sc_alt_printScreen(
|
||||||
|
0x54), /*
|
||||||
|
* Alt
|
||||||
|
* +
|
||||||
|
* print
|
||||||
|
* screen.
|
||||||
|
* MapVirtualKeyEx(
|
||||||
|
* VK_SNAPSHOT
|
||||||
|
* )
|
||||||
|
* ,
|
||||||
|
* MAPVK_VK_TO_VSC_EX
|
||||||
|
* )
|
||||||
|
* ,
|
||||||
|
* 0
|
||||||
|
* )
|
||||||
|
* returns
|
||||||
|
* scancode
|
||||||
|
* 0x54.
|
||||||
|
*/
|
||||||
|
sc_bracketAngle(0x56), /* Key between the left shift and Z. */
|
||||||
|
sc_f11(0x57), sc_f12(0x58), sc_oem_1(0x5a), /* VK_OEM_WSCTRL */
|
||||||
|
sc_oem_2(0x5b), /* VK_OEM_FINISH */
|
||||||
|
sc_oem_3(0x5c), /* VK_OEM_JUMP */
|
||||||
|
sc_eraseEOF(0x5d), sc_oem_4(0x5e), /* VK_OEM_BACKTAB */
|
||||||
|
sc_oem_5(0x5f), /* VK_OEM_AUTO */
|
||||||
|
sc_zoom(0x62), sc_help(0x63), sc_f13(0x64), sc_f14(0x65), sc_f15(0x66), sc_f16(0x67), sc_f17(0x68), sc_f18(
|
||||||
|
0x69), sc_f19(
|
||||||
|
0x6a), sc_f20(0x6b), sc_f21(0x6c), sc_f22(0x6d), sc_f23(0x6e), sc_oem_6(0x6f), /* VK_OEM_PA3 */
|
||||||
|
sc_katakana(0x70), sc_oem_7(0x71), /* VK_OEM_RESET */
|
||||||
|
sc_f24(0x76), sc_sbcschar(0x77), sc_convert(0x79), sc_nonconvert(0x7B), /* VK_OEM_PA1 */
|
||||||
|
|
||||||
|
sc_media_previous(0xE010), sc_media_next(0xE019), sc_numpad_enter(0xE01C), sc_controlRight(0xE01D), sc_volume_mute(
|
||||||
|
0xE020), sc_launch_app2(0xE021), sc_media_play(0xE022), sc_media_stop(
|
||||||
|
0xE024), sc_volume_down(0xE02E), sc_volume_up(
|
||||||
|
0xE030), sc_browser_home(0xE032), sc_numpad_divide(0xE035), sc_printScreen(0xE037),
|
||||||
|
/*
|
||||||
|
* sc_printScreen: - make: 0xE02A 0xE037 - break: 0xE0B7 0xE0AA - MapVirtualKeyEx( VK_SNAPSHOT), MAPVK_VK_TO_VSC_EX), 0 ) returns scancode 0x54; - There is no VK_KEYDOWN with VK_SNAPSHOT.
|
||||||
|
*/
|
||||||
|
sc_altRight(0xE038), sc_cancel(0xE046), /* CTRL + Pause */
|
||||||
|
sc_home(0xE047), sc_arrowUp(0xE048), sc_pageUp(0xE049), sc_arrowLeft(0xE04B), sc_arrowRight(0xE04D), sc_end(
|
||||||
|
0xE04F), sc_arrowDown(0xE050), sc_pageDown(0xE051), sc_insert(0xE052), sc_delete(0xE053), sc_metaLeft(
|
||||||
|
0xE05B), sc_metaRight(0xE05C), sc_application(0xE05D), sc_power(0xE05E), sc_sleep(0xE05F), sc_wake(
|
||||||
|
0xE063), sc_browser_search(0xE065), sc_browser_favorites(0xE066), sc_browser_refresh(
|
||||||
|
0xE067), sc_browser_stop(0xE068), sc_browser_forward(0xE069), sc_browser_back(
|
||||||
|
0xE06A), sc_launch_app1(
|
||||||
|
0xE06B), sc_launch_email(0xE06C), sc_launch_media(0xE06D),
|
||||||
|
|
||||||
|
sc_pause(0xE11D45);
|
||||||
|
/*
|
||||||
|
* sc_pause: - make: 0xE11D 45 0xE19D C5 - make in raw input: 0xE11D 0x45 - break: none - No repeat when you hold the key down - There are no break so I don't know how the key down/up is expected
|
||||||
|
* to work. Raw input sends "keydown" and "keyup" messages), and it appears that the keyup message is sent directly after the keydown message (you can't hold the key down) so depending on when
|
||||||
|
* GetMessage or PeekMessage will return messages), you may get both a keydown and keyup message "at the same time". If you use VK messages most of the time you only get keydown messages), but
|
||||||
|
* some times you get keyup messages too. - when pressed at the same time as one or both control keys, generates a 0xE046 (sc_cancel) and the string for that scancode is "break".
|
||||||
|
*/
|
||||||
|
|
||||||
|
public int Code;
|
||||||
|
|
||||||
|
Scancode(int code) {
|
||||||
|
Code = code;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in a new issue