From 32cea5c38415bed38cdd8e0ed64ea7e311aa8f78 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 3 Aug 2020 02:43:31 +0200 Subject: [PATCH] Attempts to fix screen on Ubuntu after /c fix It stops sending updates after running the fix command --- .../java/sznp/virtualcomputer/Computer.java | 74 ++++++++++++---- .../renderer/MCFrameBuffer.java | 87 +++++++++++++------ 2 files changed, 115 insertions(+), 46 deletions(-) diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java index 5728c3d..53636c2 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java @@ -3,6 +3,7 @@ package sznp.virtualcomputer; import com.google.common.collect.Lists; import lombok.Getter; import lombok.val; +import lombok.var; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -28,6 +29,7 @@ public final class Computer { private MachineEventHandler handler; private IEventListener listener; private VirtualBoxManager manager; + private MCFrameBuffer framebuffer; @java.beans.ConstructorProperties({"plugin"}) public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox) { @@ -45,7 +47,7 @@ public final class Computer { return; } Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { - if (vbox.getMachines().size() <= index) { + if (index < 0 || vbox.getMachines().size() <= index) { sendMessage(sender, "§cMachine not found!"); return; } @@ -54,7 +56,9 @@ public final class Computer { machine = vbox.getMachines().get(index); session.setName("minecraft"); // machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); - This creates a *process*, we don't want that anymore - machine.lockMachine(session, LockType.VM); // We want the machine inside *our* process <-- Need the VM type to have console access + synchronized (session) { + machine.lockMachine(session, LockType.VM); // We want the machine inside *our* process <-- Need the VM type to have console access + } VBoxEventHandler.getInstance().setup(machine.getId(), sender); //TODO: Sometimes null } catch (VBoxException e) { if (e.getResultCode() == 0x80070005) { //lockMachine: "The object functionality is limited" @@ -95,8 +99,11 @@ public final class Computer { IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2 handler.setProgress(progress); handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way? - console.getDisplay().attachFramebuffer(0L, - COMUtils.gimmeAFramebuffer(new MCFrameBuffer(console.getDisplay()))); + val fb = new MCFrameBuffer(console.getDisplay()); + String fbid = console.getDisplay().attachFramebuffer(0L, + COMUtils.gimmeAFramebuffer(fb)); + fb.setId(fbid); + framebuffer = fb; } private void sendMessage(@Nullable CommandSender sender, String message) { @@ -114,7 +121,9 @@ public final class Computer { return; } sendMessage(sender, "§eStopping computer..."); - session.getConsole().powerDown(); + synchronized (session) { + session.getConsole().powerDown(); + } } public void PowerButton(CommandSender sender, int index) { @@ -125,7 +134,9 @@ public final class Computer { if (session.getState() != SessionState.Locked || session.getMachine() == null) { Start(sender, index); } else { - session.getConsole().powerButton(); + synchronized (session) { + session.getConsole().powerButton(); + } sendMessage(sender, "§ePowerbutton pressed."); } } @@ -136,17 +147,36 @@ public final class Computer { if (checkMachineNotRunning(sender)) return; sendMessage(sender, "§eResetting computer..."); - session.getConsole().reset(); + synchronized (session) { + session.getConsole().reset(); + } sendMessage(sender, "§eComputer reset."); } public void FixScreen(CommandSender sender) { if (checkMachineNotRunning(sender)) return; + if (framebuffer == null) { + sender.sendMessage("§cFramebuffer is null..."); + return; + } + val lastUpdated = new Holder(); + var status = session.getConsole().getGuest().getFacilityStatus(AdditionsFacilityType.Seamless, lastUpdated); + sendMessage(sender, "Seamless status: " + status); sendMessage(sender, "§eFixing screen..."); - session.getConsole().getDisplay().setSeamlessMode(false); - session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L, true); + try { + synchronized (session) { + session.getConsole().getDisplay().setSeamlessMode(false); + session.getConsole().getDisplay().detachFramebuffer(0L, framebuffer.getId()); + session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L, true); + framebuffer.setId(session.getConsole().getDisplay().attachFramebuffer(0L, COMUtils.gimmeAFramebuffer(framebuffer))); + } + } catch (Exception e) { + e.printStackTrace(); + } sendMessage(sender, "§eScreen fixed."); + status = session.getConsole().getGuest().getFacilityStatus(AdditionsFacilityType.Seamless, lastUpdated); + sendMessage(sender, "Seamless status: " + status); } public boolean checkMachineNotRunning(@Nullable CommandSender sender) { @@ -177,14 +207,16 @@ public final class Computer { } // 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(plugin, sendrelease, durationorstate); + synchronized (session) { + 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(plugin, sendrelease, durationorstate * 20); + } } } @@ -195,7 +227,9 @@ public final class Computer { if (mbs.length() > 0 && down) state = Arrays.stream(MouseButtonState.values()).filter(mousebs -> mousebs.name().equalsIgnoreCase(mbs)) .findAny().orElseThrow(() -> new Exception("Unknown mouse button")).value(); - session.getConsole().getMouse().putMouseEvent(x, y, z, w, state); + synchronized (session) { + session.getConsole().getMouse().putMouseEvent(x, y, z, w, state); + } } public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs) throws Exception { @@ -212,7 +246,9 @@ public final class Computer { public void onMachineStop(CommandSender sender) { Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { if (session.getState() == SessionState.Locked) { - session.unlockMachine(); //Needs to be outside of the event handler + synchronized (session) { + session.unlockMachine(); //Needs to be outside of the event handler + } handler = null; machine = null; session = manager.getSessionObject(); diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java index 4af3520..f1548f3 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java @@ -1,7 +1,9 @@ package sznp.virtualcomputer.renderer; import com.sun.jna.Pointer; +import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import org.bukkit.Bukkit; import org.bukkit.scheduler.BukkitTask; import org.virtualbox_6_1.Holder; @@ -21,46 +23,77 @@ public class MCFrameBuffer implements IMCFrameBuffer { private Pointer pointer; private int width; private int height; + private boolean updating = false; + @Getter + @Setter + private String id; @Override public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) { if (tt != null) tt.cancel(); tt = Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> { - try { - display.querySourceBitmap(0L, holder); - long[] ptr = new long[1], w = new long[1], h = new long[1], bpp = new long[1], bpl = new long[1], pf = new long[1]; - COMUtils.queryBitmapInfo(holder.value, ptr, w, h, bpp, bpl, pf); - if (PluginMain.direct) { - pointer = new Pointer(ptr[0]); - this.width = (int) w[0]; - this.height = (int) h[0]; - GPURenderer.update(pointer.getByteArray(0L, (int) (w[0] * h[0] * 4)), (int) w[0], (int) h[0], 0, 0, this.width, this.height); - } else { - PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4); - if (width * height > 640 * 480) - PluginMain.allpixels.limit(640 * 480 * 4); - else - PluginMain.allpixels.limit((int) (width * height * 4)); + synchronized (this) { //If a change occurs twice, then wait for it + try { + System.out.println("Change: " + xOrigin + " " + yOrigin + " - " + width + " " + height); + display.querySourceBitmap(0L, holder); + long[] ptr = new long[1], w = new long[1], h = new long[1], bpp = new long[1], bpl = new long[1], pf = new long[1]; + COMUtils.queryBitmapInfo(holder.value, ptr, w, h, bpp, bpl, pf); + if (PluginMain.direct) { + pointer = new Pointer(ptr[0]); + this.width = (int) w[0]; + this.height = (int) h[0]; + if (this.width > 1024 || this.height > 768) + return; + GPURenderer.update(pointer.getByteArray(0L, (int) (w[0] * h[0] * 4)), (int) w[0], (int) h[0], 0, 0, this.width, this.height); + } else { + PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4); + if (width * height > 640 * 480) + PluginMain.allpixels.limit(640 * 480 * 4); + else + PluginMain.allpixels.limit((int) (width * height * 4)); + } + } catch (VBoxException e) { + if (e.getResultCode() == 0x80070005) + return; // Machine is being powered down + if (e.getResultCode() == 0x80004005) //The function "querySourceBitmap" returned an error condition: "Operation failed (NS_ERROR_FAILURE)" + System.out.println("I don't know why this happens, but stopping the computer helps."); + e.printStackTrace(); + } catch (Throwable t) { + t.printStackTrace(); + } finally { + System.out.println("Change finished"); } - } catch (VBoxException e) { - if (e.getResultCode() == 0x80070005) - return; // Machine is being powered down - if (e.getResultCode() == 0x80004005) //The function "querySourceBitmap" returned an error condition: "Operation failed (NS_ERROR_FAILURE)" - System.out.println("I don't know why this happens, but stopping the computer helps."); - e.printStackTrace(); - } catch (Throwable t) { - t.printStackTrace(); } }); } @Override public void notifyUpdate(long x, long y, long width, long height) { - Timing t = new Timing(); - GPURenderer.update(pointer.getByteArray(0L, this.width * this.height * 4), this.width, this.height, (int) x, (int) y, (int) width, (int) height); - if (t.elapsedMS() > 60) //Typically 1ms max - System.out.println("Update took " + t.elapsedMS() + "ms"); + if (this.width > 1024 || this.height > 768) + return; + Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> { + synchronized (this) { + if (updating) { + System.out.println("Ignoring update"); + return; + } + updating = true; + if (pointer == null) { + System.out.println("Screen pointer is null"); + updating = false; + return; + } + System.out.println("Update: " + x + " " + y + " - " + width + " " + height); + Timing t = new Timing(); + GPURenderer.update(pointer.getByteArray(0L, this.width * this.height * 4), this.width, this.height, (int) x, (int) y, (int) width, (int) height); + if (t.elapsedMS() > 60) //Typically 1ms max + System.out.println("Update took " + t.elapsedMS() + "ms"); + else + System.out.println("Update finished"); + updating = false; + } + }); } @Override