POC for new Chromium support
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src/"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
1
eclipse-extensions/org.springframework.ide.eclipse.xterm.macosx.aarch64/.gitignore
vendored
Normal file
1
eclipse-extensions/org.springframework.ide.eclipse.xterm.macosx.aarch64/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/lib/node_modules
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.springframework.ide.eclipse.xterm.macosx.aarch64</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,11 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Node-Xterm package for Mac OSX
|
||||
Bundle-SymbolicName: org.springframework.ide.eclipse.xterm.macosx.aarch64
|
||||
Bundle-Version: 4.16.0.qualifier
|
||||
Bundle-Vendor: VMware
|
||||
Fragment-Host: org.springframework.ide.eclipse.xterm;bundle-version="4.8.2"
|
||||
Automatic-Module-Name: org.springframework.ide.eclipse.xterm.macosx.aarch64
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Eclipse-PlatformFilter: (& (osgi.ws=cocoa) (osgi.os=macosx) (|(osgi.arch=aarch64)(osgi.arch=ppc)))
|
||||
Eclipse-BundleShape: dir
|
||||
@@ -0,0 +1,5 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
lib/
|
||||
1105
eclipse-extensions/org.springframework.ide.eclipse.xterm.macosx.aarch64/lib/package-lock.json
generated
Normal file
1105
eclipse-extensions/org.springframework.ide.eclipse.xterm.macosx.aarch64/lib/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"node-xterm": "^0.0.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot.ide</groupId>
|
||||
<artifactId>org.springframework.boot.ide</artifactId>
|
||||
<version>4.16.0-SNAPSHOT</version>
|
||||
<relativePath>../../eclipse-distribution/pom.xml</relativePath>
|
||||
</parent>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
<artifactId>org.springframework.ide.eclipse.xterm.macosx.aarch64</artifactId>
|
||||
|
||||
<properties>
|
||||
<archive-file>node-xterm-0.0.2-macosx-aarch64.zip</archive-file>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<filesets>
|
||||
<fileset>
|
||||
<directory>${project.basedir}</directory>
|
||||
<includes>
|
||||
<include>**/lib/**</include>
|
||||
</includes>
|
||||
<followSymlinks>false</followSymlinks>
|
||||
</fileset>
|
||||
</filesets>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- download-maven-plugin fails to download archive files for mac and linux some times. Use
|
||||
- wagon-maven-plugin for downloading
|
||||
- maven-antrun-plugin for unpacking and deleting the archive
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>wagon-maven-plugin</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>download-nodejs-app</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>download-single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<serverId>s3</serverId>
|
||||
<url>https://dist.springsource.com/release/TOOLS/node-xterm</url>
|
||||
<fromFile>${archive-file}</fromFile>
|
||||
<toFile>${project.basedir}/lib/node-xterm.zip</toFile>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>process-resources</phase>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<echo message="unzipping node-xterm.zip" />
|
||||
<unzip src="${project.basedir}/lib/node-xterm.zip"
|
||||
dest="lib/" />
|
||||
<delete>
|
||||
<fileset dir="${project.basedir}/lib"
|
||||
includes="node-xterm.zip" />
|
||||
</delete>
|
||||
</tasks>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>target-platform-configuration</artifactId>
|
||||
<version>${tycho-version}</version>
|
||||
<configuration>
|
||||
<resolver>p2</resolver>
|
||||
<pomDependencies>ignore</pomDependencies>
|
||||
<environments>
|
||||
<environment>
|
||||
<os>macosx</os>
|
||||
<ws>cocoa</ws>
|
||||
<arch>x86_64</arch>
|
||||
</environment>
|
||||
</environments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,5 +1,6 @@
|
||||
/target/
|
||||
/.settings/
|
||||
/lib/
|
||||
.DS_Store
|
||||
/**/.DS_Store
|
||||
.project
|
||||
|
||||
@@ -13,7 +13,10 @@ Require-Bundle: org.eclipse.ui;bundle-version="3.115.0",
|
||||
org.apache.httpcomponents.httpcore,
|
||||
org.eclipse.wildwebdeveloper.embedder.node
|
||||
Import-Package: javax.inject
|
||||
Bundle-ClassPath: .
|
||||
Bundle-ClassPath: .,
|
||||
lib/ipcsocket-1.4.0.jar,
|
||||
lib/jna-5.8.0.jar,
|
||||
lib/jna-platform-5.8.0.jar
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: org.springframework.ide.eclipse.xterm,
|
||||
org.springframework.ide.eclipse.xterm.views
|
||||
|
||||
@@ -4,4 +4,8 @@ bin.includes = META-INF/,\
|
||||
.,\
|
||||
plugin.xml,\
|
||||
css/,\
|
||||
icons/
|
||||
icons/,\
|
||||
lib/,\
|
||||
lib/ipcsocket-1.4.0.jar,\
|
||||
lib/jna-5.8.0.jar,\
|
||||
lib/jna-platform-5.8.0.jar
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
</parent>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
<artifactId>org.springframework.ide.eclipse.xterm</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.scala-sbt.ipcsocket</groupId>
|
||||
<artifactId>ipcsocket</artifactId>
|
||||
<version>1.4.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -60,6 +68,48 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<?m2e execute onConfiguration?>
|
||||
<id>get-libs</id>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<phase>validate</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.scala-sbt.ipcsocket</groupId>
|
||||
<artifactId>ipcsocket</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<overWrite>true</overWrite>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<version>5.8.0</version>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>5.8.0</version>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<outputDirectory>${basedir}/lib/</outputDirectory>
|
||||
<!-- baseVersion is to avoid SNAPSHOT dependencies being copied with
|
||||
ever daily changing timestamp -->
|
||||
<useBaseVersion>true</useBaseVersion>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,611 @@
|
||||
package org.springframework.ide.eclipse.chromium.browser;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.ControlEvent;
|
||||
import org.eclipse.swt.events.ControlListener;
|
||||
import org.eclipse.swt.events.DisposeEvent;
|
||||
import org.eclipse.swt.events.DisposeListener;
|
||||
import org.eclipse.swt.events.KeyEvent;
|
||||
import org.eclipse.swt.events.KeyListener;
|
||||
import org.eclipse.swt.events.MouseEvent;
|
||||
import org.eclipse.swt.events.MouseListener;
|
||||
import org.eclipse.swt.events.MouseMoveListener;
|
||||
import org.eclipse.swt.events.MouseWheelListener;
|
||||
import org.eclipse.swt.events.PaintEvent;
|
||||
import org.eclipse.swt.events.PaintListener;
|
||||
import org.eclipse.swt.graphics.Cursor;
|
||||
import org.eclipse.swt.graphics.GC;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.ImageData;
|
||||
import org.eclipse.swt.graphics.PaletteData;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.widgets.Canvas;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.scalasbt.ipcsocket.UnixDomainServerSocket;
|
||||
import org.scalasbt.ipcsocket.Win32NamedPipeServerSocket;
|
||||
import org.scalasbt.ipcsocket.Win32SecurityLevel;
|
||||
|
||||
public class ElectronBrowserCanvas extends Canvas
|
||||
{
|
||||
private Image image;
|
||||
private GC gc;
|
||||
|
||||
private Image createImage(int width, int height)
|
||||
{
|
||||
ImageData data = new ImageData(width, height, 32, new PaletteData(0xff0000,0x00ff00, 0x0000ff));
|
||||
data.setAlpha(0, 0, 0);
|
||||
Arrays.fill(data.alphaData, (byte) -1);
|
||||
Arrays.fill(data.data, (byte) -1);
|
||||
return new Image(null, data);
|
||||
}
|
||||
|
||||
public ElectronBrowserCanvas(Composite parent)
|
||||
{
|
||||
this(parent, true);
|
||||
}
|
||||
|
||||
public ElectronBrowserCanvas(Composite parent, boolean startElectron)
|
||||
{
|
||||
super(parent, SWT.NO_REDRAW_RESIZE | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND);
|
||||
|
||||
Display display = getDisplay();
|
||||
image = createImage(display.getBounds().width, display.getBounds().height);
|
||||
gc = new GC(image);
|
||||
FPSText.initGC(gc);
|
||||
|
||||
addControlListener(new ControlListener() {
|
||||
@Override
|
||||
public void controlResized(ControlEvent e)
|
||||
{
|
||||
Rectangle oldBounds = image.getBounds();
|
||||
Rectangle newBounds = getBounds();
|
||||
if (oldBounds.width < newBounds.width || oldBounds.height < newBounds.height)
|
||||
{
|
||||
image.dispose();
|
||||
image = createImage(newBounds.width, newBounds.height);
|
||||
gc = new GC(image);
|
||||
FPSText.initGC(gc);
|
||||
}
|
||||
sendResize(newBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlMoved(ControlEvent e)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
addPaintListener(new PaintListener() {
|
||||
@Override
|
||||
public void paintControl(PaintEvent event)
|
||||
{
|
||||
event.gc.drawImage(image, event.x, event.y, event.width, event.height, event.x, event.y, event.width, event.height);
|
||||
}
|
||||
});
|
||||
|
||||
addMouseListener(new MouseListener() {
|
||||
@Override
|
||||
public void mouseUp(MouseEvent e)
|
||||
{
|
||||
handleEvent("mouseUp", e);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDown(MouseEvent e)
|
||||
{
|
||||
handleEvent("mouseDown", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDoubleClick(MouseEvent e)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
addMouseMoveListener(new MouseMoveListener() {
|
||||
@Override
|
||||
public void mouseMove(MouseEvent e)
|
||||
{
|
||||
handleEvent("mouseMove", e);
|
||||
}
|
||||
});
|
||||
|
||||
addKeyListener(new KeyListener() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
handleEvent("keyUp", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
handleEvent("keyDown", e);
|
||||
handleEvent("char", e);
|
||||
}
|
||||
});
|
||||
addMouseWheelListener(new MouseWheelListener() {
|
||||
|
||||
@Override
|
||||
public void mouseScrolled(MouseEvent e)
|
||||
{
|
||||
if (e.count != 0)
|
||||
{
|
||||
handleEvent("mouseWheel", e); // TODO how to correctly calculate deltaY etc.
|
||||
}
|
||||
}
|
||||
});
|
||||
addDisposeListener(new DisposeListener() {
|
||||
@Override
|
||||
public void widgetDisposed(DisposeEvent e)
|
||||
{
|
||||
sendMessage("{\n" +
|
||||
" \"type\":\"quit\"\n" + "}");
|
||||
if (process != null) process.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
new Thread(() -> {
|
||||
listen();
|
||||
}).start();
|
||||
|
||||
if (startElectron)
|
||||
{
|
||||
unzipAndStartElectron();
|
||||
}
|
||||
}
|
||||
|
||||
private ServerSocket server;
|
||||
|
||||
private void listen()
|
||||
{
|
||||
try
|
||||
{
|
||||
boolean win = System.getProperty("os.name", "").toLowerCase().startsWith("win");
|
||||
String socketName;
|
||||
if (win)
|
||||
{
|
||||
socketName = "\\\\.\\pipe\\electron_pipe";
|
||||
server = new Win32NamedPipeServerSocket(socketName, false, Win32SecurityLevel.LOGON_DACL);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Path pipe = Files.createTempDirectory("electron_pipe").resolve("electron.pipe");
|
||||
Path pipe = Files.createDirectories(Paths.get("/tmp/electron_pipe")).resolve("electron.pipe");
|
||||
Files.deleteIfExists(pipe);
|
||||
socketName = pipe.toString();
|
||||
server = new UnixDomainServerSocket(socketName, false);
|
||||
}
|
||||
//server = new ServerSocket(9090);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.err.println("Could not open socket.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
while (!isDisposed())
|
||||
{
|
||||
run();
|
||||
try
|
||||
{
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private PrintWriter out;
|
||||
|
||||
private void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.out.println("Open connection");
|
||||
final Socket socket = server.accept();
|
||||
final InputStream inputStream = socket.getInputStream();
|
||||
final BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
|
||||
|
||||
out = new PrintWriter(socket.getOutputStream());
|
||||
|
||||
Display display = getDisplay();
|
||||
display.asyncExec(() -> sendResize(getBounds()));
|
||||
browse("");
|
||||
|
||||
FPSText fps = new FPSText();
|
||||
|
||||
String command = null;
|
||||
while (!isDisposed() && (command = new String(readBytesFromInputStream(bufferedInputStream, 32), "UTF-8")) != null)
|
||||
{
|
||||
if (command.startsWith("cursor:"))
|
||||
{
|
||||
String type = (command.substring("cursor:".length()).split(","))[0];
|
||||
display.asyncExec(() -> setCursor(type));
|
||||
}
|
||||
else if (command.startsWith("paint:"))
|
||||
{
|
||||
String[] split = command.substring("paint:".length()).split(",");
|
||||
Point point = new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
|
||||
Image newImage = new Image(null, new PartialInputStream(bufferedInputStream, Integer.parseInt(split[2])));
|
||||
Rectangle bounds = newImage.getBounds();
|
||||
if (isDisposed()) break;
|
||||
display.syncExec(() -> { // TODO sync or async?
|
||||
if (isDisposed()) return;
|
||||
gc.drawImage(newImage, point.x, point.y);
|
||||
fps.drawFPS(gc, this, point.x, point.y);
|
||||
//gc.drawRectangle(point.x, point.y, bounds.width - 1, bounds.height - 1);
|
||||
redraw(point.x, point.y, bounds.width, bounds.height, false);
|
||||
newImage.dispose();
|
||||
});
|
||||
fps.calcFPS();
|
||||
sendMessage("accept", Collections.singletonMap("imageCount", split[3]));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Invalid command");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
out = null;
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] readBytesFromInputStream(final InputStream inputStream, final int numberOfBytes) throws IOException
|
||||
{
|
||||
int bytesToRead = numberOfBytes;
|
||||
int bytesRead = 0;
|
||||
byte[] bytes = new byte[numberOfBytes];
|
||||
while (bytesToRead > 0 && (bytesRead = inputStream.read(bytes, numberOfBytes - bytesToRead, bytesToRead)) > -1)
|
||||
{
|
||||
bytesToRead -= bytesRead;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private void sendMessage(String type, Map<String, String> parameters)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
parameters.forEach((key, value) -> builder.append(",\n" + " \"" + key + "\":" + value));
|
||||
sendMessage("{\n" + " \"type\":\"" + type + "\"" + builder.toString() + "\n" + "}");
|
||||
}
|
||||
|
||||
private void sendMessage(String message)
|
||||
{
|
||||
if (out != null)
|
||||
{
|
||||
out.write(message + "\n");
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void browse(String url)
|
||||
{
|
||||
sendMessage("browse", Collections.singletonMap("url", "\"" + url + "\""));
|
||||
}
|
||||
|
||||
private void sendResize(Rectangle bounds)
|
||||
{
|
||||
Map<String, String> params = new LinkedHashMap<>();
|
||||
params.put("width", Integer.toString(bounds.width));
|
||||
params.put("height", Integer.toString(bounds.height));
|
||||
sendMessage("resize", params);
|
||||
}
|
||||
|
||||
private void handleEvent(String type, MouseEvent e)
|
||||
{
|
||||
Map<String, String> params = new LinkedHashMap<>();
|
||||
|
||||
if ((e.stateMask & SWT.BUTTON_MASK) != 0)
|
||||
{
|
||||
List<String> modifiers = new ArrayList<>();
|
||||
if ((e.stateMask & SWT.BUTTON1) != 0)
|
||||
{
|
||||
modifiers.add("\"leftButtonDown\"");
|
||||
}
|
||||
if ((e.stateMask & SWT.BUTTON2) != 0)
|
||||
{
|
||||
modifiers.add("\"middleButtonDown\"");
|
||||
}
|
||||
if ((e.stateMask & SWT.BUTTON3) != 0)
|
||||
{
|
||||
modifiers.add("\"rightButtonDown\"");
|
||||
}
|
||||
params.put("modifiers", "[" + String.join(",", modifiers) + "]");
|
||||
}
|
||||
String button;
|
||||
switch (e.button)
|
||||
{
|
||||
case 1:
|
||||
button = "left";
|
||||
break;
|
||||
case 2:
|
||||
button = "middle";
|
||||
break;
|
||||
case 3:
|
||||
button = "right";
|
||||
break;
|
||||
default:
|
||||
button = null;
|
||||
break;
|
||||
}
|
||||
|
||||
params.put("x", Integer.toString(e.x));
|
||||
params.put("y", Integer.toString(e.y));
|
||||
|
||||
/*if (oldMouseEvent != null && e.time - oldMouseEvent.time < 1000)
|
||||
{
|
||||
params.put("movementX", Integer.toString(oldMouseEvent.x - e.x));
|
||||
params.put("movementY", Integer.toString(oldMouseEvent.y - e.y));
|
||||
}
|
||||
oldMouseEvent = e;*/
|
||||
|
||||
if (button != null) params.put("button", "\"" + button + "\"");
|
||||
if (!"mouseWheel".equals(type))
|
||||
{
|
||||
params.put("clickCount", Integer.toString(e.count));
|
||||
}
|
||||
else
|
||||
{
|
||||
params.put("deltaY", Integer.toString(e.count * 100 / 3));
|
||||
}
|
||||
sendMessage(type, params);
|
||||
}
|
||||
|
||||
private void handleEvent(String type, KeyEvent e)
|
||||
{
|
||||
String keyCode = null;
|
||||
if (Character.isLetterOrDigit(e.character))
|
||||
{
|
||||
keyCode = Character.toString(e.character);
|
||||
}
|
||||
else
|
||||
{
|
||||
String c = null;
|
||||
switch (e.keyCode)
|
||||
{
|
||||
case 8:
|
||||
keyCode = "Backspace";
|
||||
break;
|
||||
case 9:
|
||||
keyCode = "Tab";
|
||||
break;
|
||||
case 13:
|
||||
keyCode = "Return";
|
||||
c = "Return";
|
||||
break;
|
||||
case 27:
|
||||
keyCode = "Escape";
|
||||
break;
|
||||
case 32:
|
||||
keyCode = "Space";
|
||||
c = " ";
|
||||
break;
|
||||
case 50:
|
||||
keyCode = "\\\"";
|
||||
c = keyCode;
|
||||
break;
|
||||
case SWT.ARROW_UP:
|
||||
keyCode = "Up";
|
||||
break;
|
||||
case SWT.ARROW_DOWN:
|
||||
keyCode = "Down";
|
||||
break;
|
||||
case SWT.ARROW_LEFT:
|
||||
keyCode = "Left";
|
||||
break;
|
||||
case SWT.ARROW_RIGHT:
|
||||
keyCode = "Right";
|
||||
break;
|
||||
case SWT.PAGE_UP:
|
||||
keyCode = "PageUp";
|
||||
break;
|
||||
case SWT.PAGE_DOWN:
|
||||
keyCode = "PageDown";
|
||||
break;
|
||||
case SWT.HOME:
|
||||
keyCode = "Home";
|
||||
break;
|
||||
case SWT.END:
|
||||
keyCode = "End";
|
||||
break;
|
||||
case SWT.KEYPAD_ADD:
|
||||
keyCode = "numadd";
|
||||
c = "+";
|
||||
break;
|
||||
case SWT.KEYPAD_SUBTRACT:
|
||||
keyCode = "numsub";
|
||||
c = "-";
|
||||
break;
|
||||
case SWT.KEYPAD_MULTIPLY:
|
||||
keyCode = "nummult";
|
||||
c = "*";
|
||||
break;
|
||||
case SWT.KEYPAD_DIVIDE:
|
||||
keyCode = "numdiv";
|
||||
c = "/";
|
||||
break;
|
||||
case SWT.KEYPAD_DECIMAL:
|
||||
keyCode = "numdec";
|
||||
c = ",";
|
||||
break;
|
||||
case SWT.KEYPAD_CR:
|
||||
keyCode = "Enter";
|
||||
break;
|
||||
case SWT.KEYPAD_0:
|
||||
case SWT.KEYPAD_1:
|
||||
case SWT.KEYPAD_2:
|
||||
case SWT.KEYPAD_3:
|
||||
case SWT.KEYPAD_4:
|
||||
case SWT.KEYPAD_5:
|
||||
case SWT.KEYPAD_6:
|
||||
case SWT.KEYPAD_7:
|
||||
case SWT.KEYPAD_8:
|
||||
case SWT.KEYPAD_9:
|
||||
keyCode = "num" + (e.keyCode - SWT.KEYPAD_0);
|
||||
break;
|
||||
case SWT.F1:
|
||||
case SWT.F2:
|
||||
case SWT.F3:
|
||||
case SWT.F4:
|
||||
case SWT.F5:
|
||||
case SWT.F6:
|
||||
case SWT.F7:
|
||||
case SWT.F8:
|
||||
case SWT.F9:
|
||||
case SWT.F10:
|
||||
case SWT.F11:
|
||||
case SWT.F12:
|
||||
keyCode = "F" + (e.keyCode - SWT.F1 + 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (keyCode == null)
|
||||
{
|
||||
if (e.character == 0)
|
||||
return;
|
||||
keyCode = Character.toString(e.character);
|
||||
}
|
||||
else if ("char".equals(type))
|
||||
{
|
||||
if (c == null)
|
||||
return;
|
||||
keyCode = c;
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage(type, Collections.singletonMap("keyCode", "\"" + keyCode + "\""));
|
||||
}
|
||||
|
||||
private Map<String, Integer> cursorMapping;
|
||||
|
||||
private void setCursor(String type)
|
||||
{
|
||||
if (cursorMapping == null)
|
||||
{
|
||||
cursorMapping = new HashMap<>();
|
||||
// TODO col-resize, row-resize, m-panning, e-panning, n-panning, ne-panning, nw-panning, s-panning, se-panning, sw-panning, w-panning, move, vertical-text, cell, context-menu, alias, progress, nodrop, copy, not-allowed, zoom-in, zoom-out, grab, grabbing
|
||||
cursorMapping.put("default", SWT.CURSOR_ARROW);
|
||||
cursorMapping.put("crosshair", SWT.CURSOR_CROSS);
|
||||
cursorMapping.put("pointer", SWT.CURSOR_HAND);
|
||||
cursorMapping.put("text", SWT.CURSOR_IBEAM);
|
||||
cursorMapping.put("wait", SWT.CURSOR_WAIT);
|
||||
cursorMapping.put("help", SWT.CURSOR_HELP);
|
||||
cursorMapping.put("e-resize", SWT.CURSOR_SIZEE);
|
||||
cursorMapping.put("n-resize", SWT.CURSOR_SIZEN);
|
||||
cursorMapping.put("ne-resize", SWT.CURSOR_SIZENE);
|
||||
cursorMapping.put("nw-resize", SWT.CURSOR_SIZENW);
|
||||
cursorMapping.put("s-resize", SWT.CURSOR_SIZES);
|
||||
cursorMapping.put("se-resize", SWT.CURSOR_SIZESE);
|
||||
cursorMapping.put("sw-resize", SWT.CURSOR_SIZESW);
|
||||
cursorMapping.put("w-resize", SWT.CURSOR_SIZEW);
|
||||
cursorMapping.put("ns-resize", SWT.CURSOR_SIZENS);
|
||||
cursorMapping.put("ew-resize", SWT.CURSOR_SIZEWE);
|
||||
cursorMapping.put("nesw-resize", SWT.CURSOR_SIZENESW);
|
||||
cursorMapping.put("nwse-resize", SWT.CURSOR_SIZENWSE);
|
||||
cursorMapping.put("none", SWT.CURSOR_NO);
|
||||
//cursorMapping.put("", SWT.CURSOR_SIZEALL);
|
||||
}
|
||||
|
||||
Integer swtType = cursorMapping.get(type);
|
||||
if (swtType == null)
|
||||
{
|
||||
System.out.println("Cursor not found:" + type);
|
||||
swtType = SWT.CURSOR_ARROW;
|
||||
}
|
||||
|
||||
Cursor cursor = new Cursor(getDisplay(), swtType);
|
||||
setCursor(cursor);
|
||||
}
|
||||
|
||||
private Process process;
|
||||
|
||||
private void unzipAndStartElectron()
|
||||
{
|
||||
File tempDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "headless-electron"); // TODO unzip to AppData
|
||||
if(!tempDir.exists())
|
||||
{
|
||||
tempDir.mkdirs();
|
||||
}
|
||||
try
|
||||
{
|
||||
// TODO add Linux and Mac
|
||||
unzip(getClass().getResourceAsStream("/headless-electron-darwin-x64-0.0.1.zip"), tempDir);
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(tempDir + File.separator + "headless-electron.app/Contents/MacOS/headless-electron");
|
||||
process = processBuilder.start();
|
||||
}
|
||||
catch (IOException e1)
|
||||
{
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void unzip(InputStream is, File dest) throws IOException
|
||||
{
|
||||
try (ZipInputStream zip = new ZipInputStream(is))
|
||||
{
|
||||
ZipEntry entry = zip.getNextEntry();
|
||||
while (entry != null)
|
||||
{
|
||||
File out = new File(dest, entry.getName());
|
||||
File parentFile = out.getParentFile();
|
||||
if (!parentFile.exists())
|
||||
{
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
if (!entry.isDirectory())
|
||||
{
|
||||
byte[] buffer = new byte[4096];
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(out)))
|
||||
{
|
||||
int count = 0;
|
||||
while ((count = zip.read(buffer)) != -1)
|
||||
{
|
||||
bos.write(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out.mkdirs();
|
||||
}
|
||||
zip.closeEntry();
|
||||
entry = zip.getNextEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.springframework.ide.eclipse.chromium.browser;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.graphics.Font;
|
||||
import org.eclipse.swt.graphics.GC;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
||||
public class FPSText
|
||||
{
|
||||
private long start = System.currentTimeMillis();
|
||||
private long frames = 0;
|
||||
private String fps = "";
|
||||
|
||||
public void calcFPS()
|
||||
{
|
||||
long end = System.currentTimeMillis();
|
||||
frames++;
|
||||
if (end - start > 1000)
|
||||
{
|
||||
fps = "FPS: " + String.format("%.2f", frames / ((end - start) / 1000.0));
|
||||
frames = 0;
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawFPS(GC gc, Composite comp, int x, int y)
|
||||
{
|
||||
Point textSize = gc.textExtent(fps);
|
||||
int width = comp.getBounds().width;
|
||||
int posX = width - textSize.x;
|
||||
if ((x > posX && y < textSize.y) || frames == 0);
|
||||
{
|
||||
gc.drawText(fps, posX, 0, false);
|
||||
comp.redraw(posX, 0, width, textSize.y, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initGC(GC gc)
|
||||
{
|
||||
gc.setFont(new Font(null,"Arial", 16, SWT.BOLD ));
|
||||
gc.setForeground(new Color(null, 255, 0, 0));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.springframework.ide.eclipse.chromium.browser;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class PartialInputStream extends FilterInputStream
|
||||
{
|
||||
|
||||
private int bytesToRead;
|
||||
|
||||
protected PartialInputStream(InputStream in, int size)
|
||||
{
|
||||
super(in);
|
||||
bytesToRead = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException
|
||||
{
|
||||
return Math.min(super.available(), bytesToRead);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
bytesToRead = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit)
|
||||
{
|
||||
throw new UnsupportedOperationException("mark not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
if (bytesToRead == 0) return -1;
|
||||
int read = super.read();
|
||||
if (read != -1) bytesToRead--;
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if (bytesToRead == 0) return -1;
|
||||
int read = super.read(b, off, Math.min(len, bytesToRead));
|
||||
bytesToRead -= read;
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException("reset not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException
|
||||
{
|
||||
long skip = super.skip(Math.min(n, bytesToRead));
|
||||
bytesToRead -= skip;
|
||||
return skip;
|
||||
}
|
||||
}
|
||||
@@ -20,16 +20,20 @@ import org.eclipse.jface.action.Action;
|
||||
import org.eclipse.jface.action.IMenuManager;
|
||||
import org.eclipse.jface.action.IToolBarManager;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.jface.layout.GridDataFactory;
|
||||
import org.eclipse.jface.layout.GridLayoutFactory;
|
||||
import org.eclipse.jface.resource.ColorRegistry;
|
||||
import org.eclipse.jface.util.IPropertyChangeListener;
|
||||
import org.eclipse.jface.util.PropertyChangeEvent;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.browser.Browser;
|
||||
import org.eclipse.swt.graphics.Font;
|
||||
import org.eclipse.swt.graphics.RGB;
|
||||
import org.eclipse.swt.graphics.RGBA;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IActionBars;
|
||||
import org.eclipse.ui.IMemento;
|
||||
import org.eclipse.ui.IViewSite;
|
||||
@@ -38,6 +42,7 @@ import org.eclipse.ui.PartInitException;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.part.ViewPart;
|
||||
import org.eclipse.ui.themes.ITheme;
|
||||
import org.springframework.ide.eclipse.chromium.browser.ElectronBrowserCanvas;
|
||||
import org.springframework.ide.eclipse.xterm.XtermPlugin;
|
||||
|
||||
public class TerminalView extends ViewPart {
|
||||
@@ -58,7 +63,8 @@ public class TerminalView extends ViewPart {
|
||||
|
||||
private Action refreshAction;
|
||||
|
||||
private Browser browser;
|
||||
private ElectronBrowserCanvas browser;
|
||||
private Text addressBar;
|
||||
|
||||
private String terminalId = DEFAULT_TERMINAL_ID;
|
||||
|
||||
@@ -68,6 +74,8 @@ public class TerminalView extends ViewPart {
|
||||
|
||||
private boolean isNewView;
|
||||
|
||||
private Composite parent;
|
||||
|
||||
private final IPropertyChangeListener PROPERTY_LISTENER = new IPropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
@@ -89,8 +97,41 @@ public class TerminalView extends ViewPart {
|
||||
|
||||
@Override
|
||||
public void createPartControl(Composite parent) {
|
||||
this.parent = parent;
|
||||
PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(PROPERTY_LISTENER);
|
||||
browser = new Browser(parent, SWT.CHROMIUM);
|
||||
|
||||
Composite shell = new Composite(parent, SWT.None);
|
||||
GridLayout layout = new GridLayout(1, false);
|
||||
layout.marginHeight = 0;
|
||||
layout.marginWidth = 0;
|
||||
shell.setLayout(layout);
|
||||
|
||||
|
||||
String url = "https://google.com";
|
||||
addressBar = new Text(shell, SWT.SINGLE);
|
||||
addressBar.setText(url);
|
||||
GridData layoutData1 = new GridData(SWT.FILL, SWT.CENTER, true, false);
|
||||
addressBar.setLayoutData(layoutData1);
|
||||
|
||||
browser = new ElectronBrowserCanvas(shell, false);
|
||||
GridData layoutData2 = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
browser.setLayoutData(layoutData2);
|
||||
|
||||
addressBar.addListener(SWT.Traverse, event -> {
|
||||
if (event.detail == SWT.TRAVERSE_RETURN)
|
||||
{
|
||||
String text = addressBar.getText();
|
||||
if (!text.startsWith("http") && !text.isEmpty())
|
||||
{
|
||||
text = "http://" + text;
|
||||
addressBar.setText(text);
|
||||
}
|
||||
browser.browse(text);
|
||||
}
|
||||
});
|
||||
|
||||
// browser = new ElectronBrowserCanvas(parent, false);
|
||||
// browser.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
|
||||
makeActions();
|
||||
contributeToActionBars();
|
||||
if (isNewView) {
|
||||
@@ -147,7 +188,10 @@ public class TerminalView extends ViewPart {
|
||||
String serviceUrl = XtermPlugin.getDefault().xtermUrl(10_000).get();
|
||||
if (Display.getCurrent() != null) {
|
||||
if (browser != null && !browser.isDisposed() && terminalId.equals(TerminalView.this.terminalId)) {
|
||||
browser.setUrl(createUrl(serviceUrl, terminalId, cmd, cwd));
|
||||
String url = createUrl(serviceUrl, terminalId, cmd, cwd);
|
||||
addressBar.setText(url);
|
||||
XtermPlugin.getDefault().getLog().info("Navigating to " + url);
|
||||
browser.browse(url);
|
||||
}
|
||||
} else {
|
||||
Display display = PlatformUI.getWorkbench().getDisplay();
|
||||
@@ -155,7 +199,9 @@ public class TerminalView extends ViewPart {
|
||||
display.asyncExec(() -> {
|
||||
if (browser != null && !browser.isDisposed() && terminalId.equals(TerminalView.this.terminalId)) {
|
||||
String url = createUrl(serviceUrl, terminalId, cmd, cwd);
|
||||
browser.setUrl(url);
|
||||
addressBar.setText(url);
|
||||
XtermPlugin.getDefault().getLog().info("Navigating to " + url);
|
||||
browser.browse(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user