mirror of
https://github.com/RaiMan/SikuliX1.git
synced 2023-06-02 10:04:55 +03:00
Merge pull request #418 from SKOORAG/appClassRefactoring
App class refactoring
This commit is contained in:
12
API/pom.xml
12
API/pom.xml
@@ -120,7 +120,7 @@
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.oneandone.reflections8</groupId>
|
||||
@@ -187,6 +187,16 @@
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.rococoa</groupId>
|
||||
<artifactId>rococoa-core</artifactId>
|
||||
<version>0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib</artifactId>
|
||||
<version>3.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
||||
107
API/src/main/java/org/sikuli/natives/GenericOsUtil.java
Normal file
107
API/src/main/java/org/sikuli/natives/GenericOsUtil.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package org.sikuli.natives;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public abstract class GenericOsUtil implements OSUtil {
|
||||
|
||||
protected static class GenericOsProcess implements OsProcess {
|
||||
|
||||
private ProcessHandle process;
|
||||
|
||||
public GenericOsProcess(ProcessHandle process) {
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPid() {
|
||||
return process.pid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return process.info().command().orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return process.isAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close(boolean force) {
|
||||
if (force) {
|
||||
process.descendants().forEach((h) -> h.destroyForcibly());
|
||||
return process.destroyForcibly();
|
||||
} else {
|
||||
process.descendants().forEach((h) -> h.destroy());
|
||||
return process.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null && other instanceof OsProcess && this.getPid() == ((OsProcess) other).getPid();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsProcess> findProcesses(String name) {
|
||||
return allProcesses().filter((p) -> FilenameUtils.getBaseName(p.getName().toLowerCase()).equals(FilenameUtils.getBaseName(name.toLowerCase())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsWindow> findWindows(String title) {
|
||||
throw new UnsupportedOperationException("findWindows not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsWindow> getWindows(OsProcess process) {
|
||||
throw new UnsupportedOperationException("getWindows not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsProcess> getProcesses() {
|
||||
return allProcesses().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsProcess open(String[] cmd, String workDir) {
|
||||
try {
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(cmd);
|
||||
|
||||
if (StringUtils.isNotBlank(workDir)) {
|
||||
pb.directory(new File(workDir));
|
||||
}
|
||||
|
||||
Process p = pb.start();
|
||||
|
||||
return new GenericOsProcess(p.toHandle());
|
||||
} catch (Exception e) {
|
||||
System.out.println("[error] WinUtil.open:\n" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsWindow getFocusedWindow() {
|
||||
throw new UnsupportedOperationException("getFocusedWindow not implemented");
|
||||
}
|
||||
|
||||
protected static Stream<OsProcess> allProcesses() {
|
||||
return ProcessHandle.allProcesses().map((h) -> new GenericOsProcess(h));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,419 +3,189 @@
|
||||
*/
|
||||
package org.sikuli.natives;
|
||||
|
||||
import org.apache.commons.exec.CommandLine;
|
||||
import org.apache.commons.exec.DefaultExecutor;
|
||||
import org.apache.commons.exec.ExecuteException;
|
||||
import org.apache.commons.exec.PumpStreamHandler;
|
||||
import org.apache.commons.exec.util.StringUtils;
|
||||
import org.sikuli.basics.Debug;
|
||||
import org.sikuli.script.App;
|
||||
import org.sikuli.script.Region;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.awt.Rectangle;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LinuxUtil implements OSUtil {
|
||||
import org.sikuli.basics.Debug;
|
||||
import org.sikuli.script.runners.ProcessRunner;
|
||||
import org.sikuli.script.support.IScriptRunner;
|
||||
|
||||
private enum SearchType {
|
||||
public class LinuxUtil extends GenericOsUtil {
|
||||
|
||||
APP_NAME,
|
||||
WINDOW_ID,
|
||||
PID
|
||||
}
|
||||
private static final class LinuxWindow implements OsWindow {
|
||||
private long id;
|
||||
|
||||
//<editor-fold desc="01 open">
|
||||
@Override
|
||||
public boolean open(App app) {
|
||||
int pid = open(app.getExec());
|
||||
app.setPID(pid);
|
||||
return pid > -1;
|
||||
}
|
||||
public LinuxWindow(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private int open(String appName) {
|
||||
try {
|
||||
String cmd[] = {"sh", "-c", "(" + appName + ") &\necho -n $!"};
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
InputStream in = p.getInputStream();
|
||||
byte pidBytes[] = new byte[64];
|
||||
int len = in.read(pidBytes);
|
||||
String pidStr = new String(pidBytes, 0, len);
|
||||
int pid = Integer.parseInt(pidStr);
|
||||
p.waitFor();
|
||||
return pid;
|
||||
//return p.exitValue();
|
||||
} catch (Exception e) {
|
||||
System.out.println("[error] openApp:\n" + e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
@Override
|
||||
public OsProcess getProcess() {
|
||||
try {
|
||||
List<String> lines = xdotool(new String[] { "getwindowpid", Long.toString(id) });
|
||||
|
||||
//<editor-fold desc="02 focus">
|
||||
String currentWindowTitle = "";
|
||||
if (!lines.isEmpty()) {
|
||||
Optional<ProcessHandle> handle = ProcessHandle.of(Long.parseLong(lines.get(0)));
|
||||
if (handle.isPresent()) {
|
||||
return new GenericOsProcess(handle.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchto(App app) {
|
||||
int ret = -1;
|
||||
currentWindowTitle = "";
|
||||
if (app.getPID() > 0) {
|
||||
ret = switchto(app.getPID(), 0);
|
||||
if (ret > -1) {
|
||||
app.setFocused(true);
|
||||
app.setWindow(currentWindowTitle);
|
||||
}
|
||||
}
|
||||
return ret > -1;
|
||||
}
|
||||
return null;
|
||||
} catch (XdotoolException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public App switchto(String title, int index) {
|
||||
//TODO switchto window title
|
||||
App app = new App();
|
||||
return app;
|
||||
}
|
||||
@Override
|
||||
public String getTitle() {
|
||||
try {
|
||||
List<String> lines = xdotool(new String[] { "getwindowname", Long.toString(id) });
|
||||
return lines.stream().findFirst().orElse("");
|
||||
} catch (XdotoolException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private int switchto(int pid, int num) {
|
||||
if (!isAvailable(wmctrlAvail, "switchApp", "wmctrl")) {
|
||||
return -1;
|
||||
}
|
||||
String winLine[] = findWindow("" + pid, num, SearchType.PID);
|
||||
if (winLine == null || winLine.length < 1) {
|
||||
return -1;
|
||||
}
|
||||
currentWindowTitle = winLine[9];
|
||||
return bringWindowToFront(winLine[0], pid);
|
||||
}
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
try {
|
||||
List<String> lines = xdotool(new String[] { "getwindowgeometry", "--shell", Long.toString(id) });
|
||||
|
||||
// private int switchtoWindow(String appName, int winNum) {
|
||||
// int windowPID = findWindowPID(appName, winNum);
|
||||
//// if (windowPID > 1) {
|
||||
//// return switchto(windowPID, winNum);
|
||||
//// }
|
||||
// if (windowPID > -1 && wmctrlLine.length > 0) {
|
||||
// return bringWindowToFront(wmctrlLine[0], windowPID);
|
||||
// }
|
||||
// System.err.println("[error] switchApp: could not identify process with search name '" + appName + "'");
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// private App switchtoWindow(String appName) {
|
||||
// int ret = switchtoWindow(appName, 0);
|
||||
// return new App(appName);
|
||||
// }
|
||||
//</editor-fold>
|
||||
if (!lines.isEmpty()) {
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
props.load(new StringReader(String.join("\n", lines)));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
//<editor-fold desc="03 close">
|
||||
@Override
|
||||
public boolean close(App app) {
|
||||
int ret;
|
||||
if (app.getPID() > 0) {
|
||||
ret = close(app.getPID());
|
||||
} else {
|
||||
ret = close(app.getExec());
|
||||
}
|
||||
return ret == 0;
|
||||
}
|
||||
return new Rectangle(Integer.parseInt(props.getProperty("X")),
|
||||
Integer.parseInt(props.getProperty("Y")), Integer.parseInt(props.getProperty("WIDTH")),
|
||||
Integer.parseInt(props.getProperty("HEIGHT")));
|
||||
}
|
||||
|
||||
private int close(int pid) {
|
||||
if (!isAvailable(wmctrlAvail, "closeApp", "wmctrl")) {
|
||||
return -1;
|
||||
}
|
||||
String winLine[] = findWindow("" + pid, 0, SearchType.PID);
|
||||
if (winLine == null) {
|
||||
return -1;
|
||||
}
|
||||
String cmd[] = {"wmctrl", "-ic", winLine[0]};
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
p.waitFor();
|
||||
return p.exitValue();
|
||||
} catch (Exception e) {
|
||||
System.out.println("[error] closeApp:\n" + e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (XdotoolException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int close(String appName) {
|
||||
try {
|
||||
//on the success exit value = 0 -> so no exception will be thrown
|
||||
CommandExecutorResult result1 = CommandExecutorHelper.execute("pidof " + appName, 0);
|
||||
String pid = result1.getStandardOutput();
|
||||
if (pid == null || pid.isEmpty()) {
|
||||
throw new CommandExecutorException("No app could be found with Name '" + appName + "'");
|
||||
}
|
||||
//use kill incase that killall could maybe not work in all environments
|
||||
return CommandExecutorHelper.execute("kill " + pid, 0).getExitValue();
|
||||
} catch (Exception e) {
|
||||
//try to search for the appName
|
||||
Integer windowPID = findWindowPID(appName, 1);
|
||||
if (windowPID > 1) {
|
||||
try {
|
||||
return CommandExecutorHelper.execute("kill " + windowPID.toString(), 0).getExitValue();
|
||||
} catch (Exception e1) {
|
||||
e.addSuppressed(e1);
|
||||
}
|
||||
}
|
||||
System.out.println("[error] closeApp:\n" + e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//</editor-fold>
|
||||
@Override
|
||||
public boolean focus() {
|
||||
try {
|
||||
xdotool(new String[] { "windowactivate", "--sync", Long.toString(id) });
|
||||
return true;
|
||||
} catch (XdotoolException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold desc="04 windows">
|
||||
@Override
|
||||
public Rectangle getFocusedWindow() {
|
||||
if (!isAvailable(xdoToolAvail, "getFocusedWindow", "xdoTool")) {
|
||||
return null;
|
||||
}
|
||||
String cmd[] = {"xdotool", "getactivewindow"};
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
InputStream in = p.getInputStream();
|
||||
BufferedReader bufin = new BufferedReader(new InputStreamReader(in));
|
||||
String str = bufin.readLine();
|
||||
long id = 0;
|
||||
if (str != null) {
|
||||
id = Integer.parseInt(str);
|
||||
String hexid = String.format("0x%08x", id);
|
||||
return findRegion(hexid, 0, SearchType.WINDOW_ID);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("[error] getFocusedWindow:\n" + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public boolean minimize() {
|
||||
throw new UnsupportedOperationException("minimize not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(App app) {
|
||||
return getWindow(app, 0);
|
||||
}
|
||||
@Override
|
||||
public boolean maximize() {
|
||||
throw new UnsupportedOperationException("maximize not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(App app, int num) {
|
||||
return getWindow(app.getPID(), num);
|
||||
}
|
||||
@Override
|
||||
public boolean restore() {
|
||||
throw new UnsupportedOperationException("restore not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(String appName) {
|
||||
return getWindow(appName, 0);
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null && other instanceof LinuxWindow && this.id == (((LinuxWindow) other).id);
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle findRegion(String appName, int winNum, SearchType type) {
|
||||
String[] winLine = findWindow(appName, winNum, type);
|
||||
if (winLine != null && winLine.length >= 7) {
|
||||
int x = new Integer(winLine[3]);
|
||||
int y = Integer.parseInt(winLine[4]);
|
||||
int w = Integer.parseInt(winLine[5]);
|
||||
int h = Integer.parseInt(winLine[6]);
|
||||
return new Rectangle(x, y, w, h);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String[] findWindow(String appName, int winNum, SearchType type) {
|
||||
String[] found = {};
|
||||
int numFound = 0;
|
||||
try {
|
||||
CommandExecutorResult result = CommandExecutorHelper.execute("wmctrl -lpGx", 0);
|
||||
|
||||
int slash = appName.lastIndexOf("/");
|
||||
if (slash >= 0) {
|
||||
// remove path: /usr/bin/....
|
||||
appName = appName.substring(slash + 1);
|
||||
}
|
||||
|
||||
if (type == SearchType.APP_NAME) {
|
||||
appName = appName.toLowerCase();
|
||||
}
|
||||
String[] lines = result.getStandardOutput().split("\\n");
|
||||
for (String str : lines) {
|
||||
//Debug.log("read: " + str);
|
||||
String winLine[] = str.split("\\s+", 10);
|
||||
boolean ok = false;
|
||||
|
||||
if (type == SearchType.WINDOW_ID) {
|
||||
if (appName.equals(winLine[0])) {
|
||||
ok = true;
|
||||
}
|
||||
} else if (type == SearchType.PID) {
|
||||
if (appName.equals(winLine[2])) {
|
||||
ok = true;
|
||||
}
|
||||
} else if (type == SearchType.APP_NAME) {
|
||||
String winLineName = winLine[7].toLowerCase();
|
||||
if (appName.equals(winLineName)) {
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok && winLine[9].toLowerCase().contains(appName)) {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (numFound >= winNum) {
|
||||
//Debug.log("Found window" + winLine);
|
||||
found = winLine;
|
||||
break;
|
||||
}
|
||||
numFound++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("[error] findWindow:\n" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PID of the givenAppname and the winNumber
|
||||
*
|
||||
* @param appName
|
||||
* @param winNum
|
||||
* @return the PID or -1 on errors
|
||||
*/
|
||||
private int findWindowPID(String appName, int winNum) {
|
||||
wmctrlLine = new String[0];
|
||||
String[] windowLine = findWindow(appName, winNum, SearchType.APP_NAME);
|
||||
if (windowLine != null && windowLine.length > 1) {
|
||||
wmctrlLine = windowLine;
|
||||
return Integer.parseInt(windowLine[2]);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
private static boolean xdotoolAvailable = true;
|
||||
private static ProcessRunner xdotoolRunner = new ProcessRunner();
|
||||
|
||||
private Rectangle getWindow(String appName, int winNum) {
|
||||
return findRegion(appName, winNum, SearchType.APP_NAME);
|
||||
}
|
||||
|
||||
private Rectangle getWindow(int pid) {
|
||||
return getWindow(pid, 0);
|
||||
}
|
||||
|
||||
private Rectangle getWindow(int pid, int winNum) {
|
||||
return findRegion("" + pid, winNum, SearchType.PID);
|
||||
}
|
||||
|
||||
private int bringWindowToFront(String windowID, int pid) {
|
||||
try {
|
||||
// execute wmctrl with hex, e.g. 'wmctrl -ia 0x00000'
|
||||
CommandExecutorHelper.execute("wmctrl -ia " + windowID, 0);
|
||||
//on the success exit value = 0 -> so no exception will be thrown
|
||||
return pid;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("[error] switchApp:\n" + e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Region> getWindows(App app) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
//</editor-fold>
|
||||
static {
|
||||
try {
|
||||
xdotool(new String[] { "-v" });
|
||||
} catch (Exception e) {
|
||||
xdotoolAvailable = false;
|
||||
Debug.error("xdotool not available.\n"
|
||||
+ "While you can use purely process based functionallity of the App class (e.g. open(), close(), isRunning()), you need to install xdotool to use window based stuff.\n"
|
||||
+ "Error message: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//<editor-fold desc="05 wmctrl xdotool">
|
||||
private static boolean wmctrlAvail = true;
|
||||
private static boolean xdoToolAvail = true;
|
||||
@SuppressWarnings("serial")
|
||||
private static class XdotoolException extends RuntimeException {
|
||||
public XdotoolException(String message) {
|
||||
super(message);
|
||||
}
|
||||
};
|
||||
|
||||
private String[] wmctrlLine = new String[0];
|
||||
private static synchronized List<String> xdotool(String[] args) {
|
||||
if (xdotoolAvailable) {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream err = new ByteArrayOutputStream();
|
||||
|
||||
@Override
|
||||
public void checkFeatureAvailability() {
|
||||
List<CommandLine> commands = Arrays.asList(
|
||||
CommandLine.parse("wmctrl -m"),
|
||||
CommandLine.parse("xdotool -v")
|
||||
);
|
||||
for (CommandLine cmd : commands) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
String commandstring = StringUtils.toString(cmd.toStrings(), " ");
|
||||
xdotoolRunner.redirect(new PrintStream(out), new PrintStream(err));
|
||||
|
||||
try {
|
||||
DefaultExecutor executor = new DefaultExecutor();
|
||||
// other return values throw exception
|
||||
executor.setExitValue(0);
|
||||
//save system output
|
||||
executor.setStreamHandler(new PumpStreamHandler(outputStream));
|
||||
executor.execute(cmd);
|
||||
} catch (ExecuteException e) {
|
||||
// it ran, but exited with non-zero status -- accept
|
||||
Debug.info("App: command %s ran, but failed: `%s'. Hoping for the best",
|
||||
commandstring, e.toString());
|
||||
} catch (IOException e) {
|
||||
String executable = cmd.toStrings()[0];
|
||||
if (executable.equals("wmctrl")) {
|
||||
wmctrlAvail = false;
|
||||
}
|
||||
if (executable.equals("xdotool")) {
|
||||
xdoToolAvail = false;
|
||||
}
|
||||
Debug.error("App: command %s is not executable, the App features will not work", executable);
|
||||
} finally {
|
||||
logCommandSysout(commandstring, outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
int exitCode = xdotoolRunner.runScript("xdotool", args, new IScriptRunner.Options());
|
||||
|
||||
private static void logCommandSysout(String commandstring, ByteArrayOutputStream outputStream) {
|
||||
//try to create some useful error output
|
||||
if (outputStream.size() > 0) {
|
||||
Debug.log(4, "command '" + commandstring + "' output:\n" + outputStream.toString());
|
||||
}
|
||||
}
|
||||
if (exitCode != 0) {
|
||||
String message = String.format("xdotool failed with code %s: %s", exitCode, err);
|
||||
Debug.trace(message);
|
||||
throw new XdotoolException(message);
|
||||
}
|
||||
|
||||
private boolean isAvailable(boolean module, String cmd, String feature) {
|
||||
if (module) {
|
||||
return true;
|
||||
}
|
||||
Debug.error("%s: feature %s: not available or not working", cmd, feature);
|
||||
return false;
|
||||
}
|
||||
//</editor-fold>
|
||||
return Arrays.asList(out.toString().trim().split("\n"));
|
||||
}
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public App get(App app) {
|
||||
int pid;
|
||||
if (app == null) {
|
||||
return app;
|
||||
}
|
||||
pid = app.getPID();
|
||||
if (!app.isClosing() && pid < 0) {
|
||||
if (app.getNameGiven() != null && !app.getNameGiven().isEmpty()) {
|
||||
pid = findWindowPID(app.getNameGiven(), 0);
|
||||
app.setPID(pid);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
if (app.isClosing() && pid > -1) {
|
||||
DefaultExecutor executor = new DefaultExecutor();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
executor.setStreamHandler(new PumpStreamHandler(outputStream));
|
||||
CommandLine command = CommandLine.parse("ps -p " + pid);
|
||||
try {
|
||||
executor.execute(command);
|
||||
app.setPID(pid);
|
||||
} catch (Exception e) {
|
||||
if (outputStream.toString().split("\\n").length == 1) {
|
||||
app.setPID(-1);
|
||||
app.setWindow("");
|
||||
} else {
|
||||
Debug.log(3, "[error] LinuxUtil::executeCommand: %s (%s)", command, e.getMessage());
|
||||
logCommandSysout(command.toString(), outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}
|
||||
@Override
|
||||
public List<OsWindow> findWindows(String title) {
|
||||
try {
|
||||
List<String> lines = xdotool(new String[] { "search", "--onlyvisible", "--name", title });
|
||||
return lines.stream().map((l) -> new LinuxWindow(Long.parseLong(l))).collect(Collectors.toList());
|
||||
} catch (XdotoolException e) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<App> getApps(String name) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public List<OsWindow> getWindows(OsProcess process) {
|
||||
try {
|
||||
List<String> lines = xdotool(
|
||||
new String[] { "search", "--onlyvisible", "--pid", Long.toString(process.getPid()) });
|
||||
return lines.stream().map((l) -> new LinuxWindow(Long.parseLong(l))).collect(Collectors.toList());
|
||||
} catch (XdotoolException e) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsWindow getFocusedWindow() {
|
||||
try {
|
||||
List<String> lines = xdotool(new String[] { "getactivewindow" });
|
||||
|
||||
if (!lines.isEmpty()) {
|
||||
return new LinuxWindow(Long.parseLong(lines.get(0)));
|
||||
}
|
||||
} catch (XdotoolException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,312 +3,169 @@
|
||||
*/
|
||||
package org.sikuli.natives;
|
||||
|
||||
import org.sikuli.basics.Debug;
|
||||
import org.sikuli.script.*;
|
||||
import org.sikuli.script.runners.AppleScriptRunner;
|
||||
import org.sikuli.script.support.IScriptRunner;
|
||||
import org.sikuli.script.support.RunTime;
|
||||
import org.sikuli.script.support.Runner;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MacUtil implements OSUtil {
|
||||
import org.sikuli.natives.mac.jna.CoreGraphics;
|
||||
import org.sikuli.natives.mac.jna.NSRunningApplication;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.platform.mac.CoreFoundation;
|
||||
import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef;
|
||||
import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef;
|
||||
import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef;
|
||||
import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;
|
||||
|
||||
private static boolean _askedToEnableAX = false;
|
||||
public class MacUtil extends GenericOsUtil {
|
||||
private static final class MacWindow implements OsWindow {
|
||||
private long number;
|
||||
private String title;
|
||||
private long pid;
|
||||
private Rectangle bounds;
|
||||
|
||||
@Override
|
||||
public void checkFeatureAvailability() {
|
||||
RunTime.get().loadLibrary("MacUtil");
|
||||
}
|
||||
public MacWindow(long number, String title, long pid, Rectangle bounds) {
|
||||
this.number = number;
|
||||
this.title = title;
|
||||
this.pid = pid;
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
tell application "System Events"
|
||||
set found to "NotFound"
|
||||
try
|
||||
set found to first item of (processes whose name is "#APP#")
|
||||
set found to first item of (processes whose unix id is equal to #PID#)
|
||||
end try
|
||||
found
|
||||
end tell
|
||||
if not found is equal to "NotFound" then
|
||||
set windowName to ""
|
||||
try
|
||||
set windowName to name of first window of application "#APP#"
|
||||
end try
|
||||
set found to {name of found, «class idux» of found, windowName}
|
||||
end if
|
||||
found
|
||||
*/
|
||||
static String cmd = "set found to \"NotFound\"\n"
|
||||
+ "try\n"
|
||||
+ "tell application \"System Events\"\n"
|
||||
+ "#LINE#\n"
|
||||
+ "end tell\n"
|
||||
+ "if not (found is equal to \"NotFound\") then\n"
|
||||
+ "set windowName to \"\"\n"
|
||||
+ "try\n"
|
||||
+ "set windowName to name of first window of found\n"
|
||||
+ "end try\n"
|
||||
+ "set found to {name of found, «class idux» of found, windowName, file of found}\n"
|
||||
+ "end if\n"
|
||||
+ "end try\n"
|
||||
+ "found\n";
|
||||
static String cmdLineApp = "set found to first item of (processes whose displayed name is \"#APP#\")";
|
||||
static String cmdLinePID = "set found to first item of (processes whose unix id is equal to #PID#)";
|
||||
@Override
|
||||
public OsProcess getProcess() {
|
||||
Optional<ProcessHandle> handle = ProcessHandle.of(pid);
|
||||
if (handle.isPresent()) {
|
||||
return new GenericOsProcess(handle.get());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
set theWindows to {}
|
||||
repeat with win in (windows of application "#APP#" whose visible is true)
|
||||
copy {name of win, bounds of win} to end of theWindows
|
||||
end repeat
|
||||
theWindows
|
||||
*/
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
private static final IScriptRunner.Options SILENT_OPTIONS = new IScriptRunner.Options().setSilent(true);
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
String cmdGetWindows = "set theWindows to {}\n" +
|
||||
"repeat with win in (windows of application \"#APP#\" whose visible is true)\n" +
|
||||
"copy {name of win, bounds of win} to end of theWindows\n" +
|
||||
"end repeat\n" +
|
||||
"theWindows\n";
|
||||
@Override
|
||||
public boolean focus() {
|
||||
NSRunningApplication app = NSRunningApplication.CLASS.runningApplicationWithProcessIdentifier((int) pid);
|
||||
|
||||
@Override
|
||||
public App get(App app) {
|
||||
String name = app.getName();
|
||||
int pid = app.getPID();
|
||||
String theCmd = "";
|
||||
if (pid < 0) {
|
||||
if (!name.isEmpty()) {
|
||||
List<App> apps = getApps(name);
|
||||
if (apps.size() > 0) {
|
||||
App theApp = apps.get(0);
|
||||
app.setPID(theApp.getPID());
|
||||
app.setName(theApp.getName());
|
||||
app.setToken(theApp.getToken());
|
||||
app.setExec(theApp.getExec());
|
||||
app.setWindow(theApp.getWindowTitle());
|
||||
}
|
||||
}
|
||||
return app;
|
||||
} else {
|
||||
theCmd = cmd.replace("#LINE#", cmdLinePID);
|
||||
theCmd = theCmd.replaceAll("#PID#", "" + pid);
|
||||
int retVal = Runner.getRunner(AppleScriptRunner.class).evalScript(theCmd, SILENT_OPTIONS);
|
||||
String result = RunTime.get().getLastCommandResult().trim();
|
||||
if (retVal > -1) {
|
||||
if (!result.contains("NotFound")) {
|
||||
String[] parts = result.split(",");
|
||||
if (parts.length > 1) {
|
||||
app.setName(parts[0].trim());
|
||||
app.setPID(parts[1].trim());
|
||||
}
|
||||
if (parts.length > 2) {
|
||||
app.setWindow(parts[2].trim());
|
||||
}
|
||||
if (parts.length > 3) {
|
||||
for (int i = 3; i < parts.length; i++) {
|
||||
String part = parts[i].trim();
|
||||
if (part.startsWith("alias ")) {
|
||||
String[] folders = part.split(":");
|
||||
part = "";
|
||||
for (int nf = 1; nf < folders.length; nf++) {
|
||||
part += "/" + folders[nf];
|
||||
}
|
||||
app.setExec(part);
|
||||
continue;
|
||||
}
|
||||
app.setWindow(app.getWindowTitle() + "," + parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
app.reset();
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}
|
||||
if (app != null) {
|
||||
return app.activateWithOptions(NSRunningApplication.NSApplicationActivationOptions.NSApplicationActivateAllWindows | NSRunningApplication.NSApplicationActivationOptions.NSApplicationActivateIgnoringOtherApps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(App app) {
|
||||
String appName = app.getExec().isEmpty() ? app.getName() : app.getExec();
|
||||
String cmd = "open -a \"" + appName + "\"";
|
||||
if (!app.getOptions().isEmpty()) {
|
||||
cmd += " --args " + app.getOptions();
|
||||
}
|
||||
int ret = shRun(cmd);
|
||||
return ret == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchto(App app) {
|
||||
if (app.isValid()) {
|
||||
//osascript -e "tell app \"safari\" to activate"
|
||||
String cmd = "tell application \""
|
||||
+ app.getName()
|
||||
+ "\" to activate";
|
||||
return 0 == Runner.getRunner(AppleScriptRunner.class).evalScript(cmd, SILENT_OPTIONS);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean minimize() {
|
||||
throw new UnsupportedOperationException("minimize not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public App switchto(String title, int index) {
|
||||
//TODO switchTo window title
|
||||
return new App();
|
||||
}
|
||||
@Override
|
||||
public boolean maximize() {
|
||||
throw new UnsupportedOperationException("maximize not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close(App app) {
|
||||
int ret;
|
||||
if (app.getPID() > -1) {
|
||||
ret = close(app.getPID());
|
||||
} else {
|
||||
ret = close(app.getExec().startsWith(app.getName()) ? app.getName() : app.getExec());
|
||||
}
|
||||
if (ret == 0) {
|
||||
app.reset();
|
||||
}
|
||||
return ret == 0;
|
||||
}
|
||||
@Override
|
||||
public boolean restore() {
|
||||
throw new UnsupportedOperationException("restore not implemented");
|
||||
}
|
||||
|
||||
private static int shRun(String sCmd) {
|
||||
String cmd[] = {"sh", "-c", sCmd};
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
p.waitFor();
|
||||
return p.exitValue();
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null && other instanceof MacWindow && this.number == (((MacWindow) other).number);
|
||||
}
|
||||
}
|
||||
|
||||
private int close(String appName) {
|
||||
String cmd = "ps aux | grep \"" + appName + "\" | grep -v \"grep\" | awk '{print $2}' | xargs kill";
|
||||
return shRun(cmd);
|
||||
}
|
||||
@Override
|
||||
public List<OsWindow> findWindows(String title) {
|
||||
return allWindows().stream().filter((w) -> w.getTitle().contains(title)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private int close(int pid) {
|
||||
String cmd = "kill " + pid;
|
||||
return shRun(cmd);
|
||||
}
|
||||
@Override
|
||||
public OsWindow getFocusedWindow() {
|
||||
return allWindows().stream().filter((w) -> {
|
||||
OsProcess process = w.getProcess();
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(App app) {
|
||||
return getWindow(app, 0);
|
||||
}
|
||||
if (process != null) {
|
||||
NSRunningApplication app = NSRunningApplication.CLASS
|
||||
.runningApplicationWithProcessIdentifier((int) w.getProcess().getPid());
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(App app, int winNum) {
|
||||
int pid = getPID(app.getName());
|
||||
return getWindow(pid, winNum);
|
||||
}
|
||||
if (app != null) {
|
||||
if (app.isActive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(String appName) {
|
||||
return getWindow(new App(appName), 0);
|
||||
}
|
||||
@Override
|
||||
public List<OsWindow> getWindows(OsProcess process) {
|
||||
if (process != null) {
|
||||
return allWindows().stream().filter((w) -> process.equals(w.getProcess())).collect(Collectors.toList());
|
||||
}
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
private Rectangle getWindow(int pid) {
|
||||
return getWindow(pid, 0);
|
||||
}
|
||||
private static List<OsWindow> allWindows() {
|
||||
ArrayList<OsWindow> windows = new ArrayList<>();
|
||||
|
||||
private Rectangle getWindow(int pid, int winNum) {
|
||||
Rectangle rect = getRegion(pid, winNum);
|
||||
return rect;
|
||||
}
|
||||
CFArrayRef windowInfo = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(
|
||||
CoreGraphics.kCGWindowListExcludeDesktopElements | CoreGraphics.kCGWindowListOptionOnScreenOnly, 0);
|
||||
|
||||
@Override
|
||||
public Rectangle getFocusedWindow() {
|
||||
Rectangle rect = getFocusedRegion();
|
||||
return rect;
|
||||
}
|
||||
try {
|
||||
int numWindows = windowInfo.getCount();
|
||||
for (int i = 0; i < numWindows; i++) {
|
||||
Pointer pointer = windowInfo.getValueAtIndex(i);
|
||||
CFDictionaryRef windowRef = new CFDictionaryRef(pointer);
|
||||
|
||||
@Override
|
||||
public List<Region> getWindows(App app) {
|
||||
List<Region> windows = new ArrayList<>();
|
||||
String theCmd = cmdGetWindows.replace("#APP#", app.getName());
|
||||
int retVal = Runner.getRunner(AppleScriptRunner.class).evalScript(theCmd, SILENT_OPTIONS);
|
||||
String result = RunTime.get().getLastCommandResult().trim();
|
||||
if (retVal > -1 && !result.isEmpty()) {
|
||||
Debug.trace("getWindows: %s", result);
|
||||
String[] parts = result.split(",");
|
||||
int lenResult = parts.length;
|
||||
if (lenResult % 5 != 0) {
|
||||
Debug.error("getWindow: at least one window title has a comma - giving up: %s", result);
|
||||
return windows;
|
||||
}
|
||||
for (int nWin = 0; nWin < lenResult; nWin += 5) {
|
||||
try {
|
||||
int x = Integer.parseInt(parts[nWin + 1].trim());
|
||||
int y = Integer.parseInt(parts[nWin + 2].trim());
|
||||
int w = Integer.parseInt(parts[nWin + 3].trim()) - x;
|
||||
int h = Integer.parseInt(parts[nWin + 4].trim()) - y;
|
||||
Region reg = new Region(x, y, w, h);
|
||||
reg.setName(parts[nWin]);
|
||||
windows.add(reg);
|
||||
} catch (NumberFormatException e) {
|
||||
Debug.error("getWindow: invalid coordinates: %s", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
Pointer numberPointer = windowRef.getValue(CoreGraphics.kCGWindowNumber);
|
||||
long windowNumber = new CFNumberRef(numberPointer).longValue();
|
||||
|
||||
@Override
|
||||
public List<App> getApps(String name) {
|
||||
new App();
|
||||
String cmd = "tell application \"System Events\"\n" +
|
||||
"set plist to (processes whose background only is false)\n" +
|
||||
"set resultlist to {}\n" +
|
||||
"repeat with n from 1 to the length of plist\n" +
|
||||
"set proc to item n of plist\n" +
|
||||
"set pwin to \"\"\ntry\nset pwin to name of first window of proc\nend try\n" +
|
||||
"set entry to {pwin as text, \"|||\", «class idux» of proc as text," +
|
||||
"displayed name of proc as text, name of proc as text, get file of proc, \"###\"}\n" +
|
||||
"set end of resultlist to entry\n" +
|
||||
"end repeat\n" +
|
||||
"end tell\n" +
|
||||
"resultlist";
|
||||
int retVal = Runner.getRunner(AppleScriptRunner.class).evalScript(cmd, SILENT_OPTIONS);
|
||||
String result = RunTime.get().getLastCommandResult().trim();
|
||||
String[] processes = (", " + result).split(", ###");
|
||||
List<App> appList = new ArrayList<>();
|
||||
int pid = 0;
|
||||
for (String process : processes) {
|
||||
if (process.startsWith(", ")) {
|
||||
process = process.substring(2);
|
||||
}
|
||||
App theApp = new App();
|
||||
String[] parts = process.split(", \\|\\|\\|,");
|
||||
String pWin = parts[0].trim();
|
||||
parts = parts[1].split(",");
|
||||
try {
|
||||
pid = Integer.parseInt(parts[0].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
pid--;
|
||||
}
|
||||
String dispName = parts[1].trim();
|
||||
String procName = parts[2].trim();
|
||||
String[] pAlias = parts[3].split(":");
|
||||
String pExec = pAlias[pAlias.length - 1];
|
||||
String pToken = String.format("%s|%s|%s", dispName, procName, pExec);
|
||||
if (name.isEmpty() || pToken.toUpperCase().contains(name.toUpperCase())) {
|
||||
theApp.setName(dispName);
|
||||
theApp.setPID(pid);
|
||||
theApp.setToken(pToken);
|
||||
theApp.setExec(pExec);
|
||||
theApp.setWindow(pWin);
|
||||
appList.add(theApp);
|
||||
}
|
||||
}
|
||||
return appList;
|
||||
}
|
||||
Pointer pidPointer = windowRef.getValue(CoreGraphics.kCGWindowOwnerPID);
|
||||
long windowPid = new CFNumberRef(pidPointer).longValue();
|
||||
|
||||
public static native int getPID(String appName);
|
||||
String windowName = "";
|
||||
Pointer namePointer = windowRef.getValue(CoreGraphics.kCGWindowName);
|
||||
|
||||
public static native Rectangle getRegion(int pid, int winNum);
|
||||
if (namePointer != null) {
|
||||
CFStringRef nameRef = new CFStringRef(namePointer);
|
||||
|
||||
public static native Rectangle getFocusedRegion();
|
||||
if (CoreFoundation.INSTANCE.CFStringGetLength(nameRef).intValue() > 0) {
|
||||
windowName = new CFStringRef(namePointer).stringValue();
|
||||
}
|
||||
}
|
||||
|
||||
Pointer boundsPointer = windowRef.getValue(CoreGraphics.kCGWindowBounds);
|
||||
CoreGraphics.CGRectRef rect = new CoreGraphics.CGRectRef();
|
||||
|
||||
boolean result = CoreGraphics.INSTANCE.CGRectMakeWithDictionaryRepresentation(boundsPointer, rect);
|
||||
|
||||
Rectangle javaRect = null;
|
||||
|
||||
if (result) {
|
||||
int x = (int) rect.origin.x;
|
||||
int y = (int) rect.origin.y;
|
||||
int width = (int) rect.size.width;
|
||||
int height = (int) rect.size.height;
|
||||
|
||||
javaRect = new Rectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
windows.add(new MacWindow(windowNumber, windowName, windowPid, javaRect));
|
||||
}
|
||||
} finally {
|
||||
windowInfo.release();
|
||||
}
|
||||
|
||||
return windows;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,43 +3,52 @@
|
||||
*/
|
||||
package org.sikuli.natives;
|
||||
|
||||
import org.sikuli.script.App;
|
||||
import org.sikuli.script.Region;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Window;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface OSUtil {
|
||||
// Windows: returns PID, 0 if fails
|
||||
// Others: return 0 if succeeds, -1 if fails
|
||||
|
||||
/**
|
||||
* check if needed command libraries or packages are installed and working<br>
|
||||
* if not ok, respective features will do nothing but issue error messages
|
||||
*/
|
||||
void checkFeatureAvailability();
|
||||
public interface OsProcess {
|
||||
long getPid();
|
||||
|
||||
App get(App app);
|
||||
String getName();
|
||||
|
||||
List<App> getApps(String name);
|
||||
boolean isRunning();
|
||||
|
||||
boolean open(App app);
|
||||
boolean close(boolean force);
|
||||
}
|
||||
|
||||
boolean switchto(App app);
|
||||
public interface OsWindow {
|
||||
OsProcess getProcess();
|
||||
|
||||
App switchto(String title, int index);
|
||||
String getTitle();
|
||||
|
||||
boolean close(App app);
|
||||
Rectangle getBounds();
|
||||
|
||||
Rectangle getWindow(String titel);
|
||||
boolean focus();
|
||||
|
||||
Rectangle getWindow(App app);
|
||||
boolean minimize();
|
||||
|
||||
Rectangle getWindow(App app, int winNum);
|
||||
boolean maximize();
|
||||
|
||||
Rectangle getFocusedWindow();
|
||||
boolean restore();
|
||||
}
|
||||
|
||||
List<Region> getWindows(App app);
|
||||
/**
|
||||
* check if needed command libraries or packages are installed and working<br>
|
||||
* if not ok, respective features will do nothing but issue error messages
|
||||
*/
|
||||
void init();
|
||||
|
||||
List<OsProcess> findProcesses(String name);
|
||||
|
||||
List<OsWindow> findWindows(String title);
|
||||
|
||||
List<OsWindow> getWindows(OsProcess process);
|
||||
|
||||
List<OsProcess> getProcesses();
|
||||
|
||||
OsProcess open(String[] cmd, String workDir);
|
||||
|
||||
OsWindow getFocusedWindow();
|
||||
}
|
||||
|
||||
@@ -33,9 +33,10 @@ public class SysUtil {
|
||||
public static OSUtil getOSUtil() {
|
||||
if (osUtil == null) {
|
||||
try {
|
||||
Class c = Class.forName(SysUtil.getOSUtilClass());
|
||||
Constructor constr = c.getConstructor();
|
||||
Class<?> c = Class.forName(SysUtil.getOSUtilClass());
|
||||
Constructor<?> constr = c.getConstructor();
|
||||
osUtil = (OSUtil) constr.newInstance();
|
||||
osUtil.init();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("SikuliX: fatal: getOSUtil:" + e.getMessage()));
|
||||
}
|
||||
|
||||
@@ -3,533 +3,196 @@
|
||||
*/
|
||||
package org.sikuli.natives;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.Tlhelp32;
|
||||
import com.sun.jna.platform.win32.User32;
|
||||
import com.sun.jna.platform.win32.WinDef.DWORD;
|
||||
import com.sun.jna.platform.win32.WinDef.HWND;
|
||||
import com.sun.jna.platform.win32.WinDef.RECT;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
import com.sun.jna.platform.win32.WinUser;
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.sikuli.script.App;
|
||||
import org.sikuli.script.Region;
|
||||
import org.sikuli.script.runners.ProcessRunner;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WinUtil implements OSUtil {
|
||||
|
||||
static final User32 user32 = User32.INSTANCE;
|
||||
|
||||
//<editor-fold desc="02 implementing interface">
|
||||
@Override
|
||||
public void checkFeatureAvailability() {
|
||||
//RunTime.loadLibrary("WinUtil");
|
||||
}
|
||||
|
||||
@Override
|
||||
public App get(App app) {
|
||||
App.log("Win:get(App): %s", app);
|
||||
if (app.getPID() > 0) {
|
||||
app = getTaskByPID(app);
|
||||
} else {
|
||||
app = getTaskByName(app);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<App> getApps(String name) {
|
||||
List<App> apps = new ArrayList<>();
|
||||
|
||||
List<ProcessInfo> processes = allProcesses();
|
||||
|
||||
for (ProcessInfo p : processes) {
|
||||
if (p.getImageName().toLowerCase().contains(name.toLowerCase())) {
|
||||
String winTitle = getTopWindowTitle(p.getPid());
|
||||
if (winTitle == null) continue;
|
||||
App theApp = new App();
|
||||
theApp.setName(p.getImageName());
|
||||
theApp.setWindow(winTitle);
|
||||
theApp.setPID(p.getPid());
|
||||
apps.add(theApp);
|
||||
}
|
||||
}
|
||||
return apps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(App app) {
|
||||
if (app.isValid()) {
|
||||
return 0 == switchApp(app.getPID(), 0);
|
||||
} else {
|
||||
String cmd = app.getExec();
|
||||
String workDir = app.getWorkDir();
|
||||
if (!app.getOptions().isEmpty()) {
|
||||
return ProcessRunner.startApp(cmd, workDir, app.getOptions());
|
||||
} else {
|
||||
return ProcessRunner.startApp(cmd, workDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean switchto(App app) {
|
||||
if (!app.isValid()) {
|
||||
return false;
|
||||
}
|
||||
int loopCount = 0;
|
||||
while (loopCount < 100) {
|
||||
int pid = switchApp(app.getPID(), 0);
|
||||
if (pid > 0) {
|
||||
if (pid == app.getPID()) {
|
||||
app.setFocused(true);
|
||||
getTaskByPID(app);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
loopCount++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public App switchto(String title, int index) {
|
||||
App app = new App();
|
||||
int pid = switchApp(title, index);
|
||||
if (pid > 0) {
|
||||
app.setPID(pid);
|
||||
return getTaskByPID(app);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private int switchApp(String appName, int num) {
|
||||
List<WindowInfo> windows = getWindowsForName(appName);
|
||||
|
||||
if (windows.size() > num) {
|
||||
return switchAppWindow(windows.get(num));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int switchApp(int pid, int num) {
|
||||
List<WindowInfo> windows = getWindowsForPid(pid);
|
||||
|
||||
if (windows.size() > num) {
|
||||
return switchAppWindow(windows.get(num));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int switchAppWindow(WindowInfo window) {
|
||||
HWND hwnd = window.getHwnd();
|
||||
|
||||
WinUser.WINDOWPLACEMENT lpwndpl = new WinUser.WINDOWPLACEMENT();
|
||||
|
||||
user32.GetWindowPlacement(hwnd, lpwndpl);
|
||||
|
||||
if (lpwndpl.showCmd == WinUser.SW_SHOWMINIMIZED || lpwndpl.showCmd == WinUser.SW_MINIMIZE) {
|
||||
user32.ShowWindow(hwnd, WinUser.SW_RESTORE);
|
||||
}
|
||||
|
||||
boolean success = user32.SetForegroundWindow(hwnd);
|
||||
|
||||
if (success) {
|
||||
user32.SetFocus(hwnd);
|
||||
IntByReference windowPid = new IntByReference();
|
||||
user32.GetWindowThreadProcessId(hwnd, windowPid);
|
||||
|
||||
return windowPid.getValue();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close(App app) {
|
||||
return ProcessRunner.closeApp("" + app.getPID());
|
||||
// if (closeApp(app.getPID()) == 0) {
|
||||
// app.reset();
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(App app) {
|
||||
return getWindow(app, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(App app, int winNum) {
|
||||
get(app);
|
||||
if (!app.isValid()) {
|
||||
return null;
|
||||
}
|
||||
HWND hwnd = getHwnd(app.getPID(), winNum);
|
||||
return hwnd != null ? getRectangle(hwnd, winNum) : null;
|
||||
//return getWindow(app.getPID(), winNum);
|
||||
}
|
||||
|
||||
// private Rectangle getWindow(int pid, int winNum) {
|
||||
// HWND hwnd = getHwnd(pid, winNum);
|
||||
// return hwnd != null ? getRectangle(hwnd, winNum) : null;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public Rectangle getWindow(String title) {
|
||||
HWND hwnd = getHwnd(title, 0);
|
||||
return hwnd != null ? getRectangle(hwnd, 0) : null;
|
||||
//return getWindow(title, 0);
|
||||
}
|
||||
|
||||
// private Rectangle getWindow(String title, int winNum) {
|
||||
// HWND hwnd = getHwnd(title, winNum);
|
||||
// return hwnd != null ? getRectangle(hwnd, winNum) : null;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public Rectangle getFocusedWindow() {
|
||||
HWND hwnd = user32.GetForegroundWindow();
|
||||
RECT rect = new User32.RECT();
|
||||
boolean success = user32.GetWindowRect(hwnd, rect);
|
||||
return success ? rect.toRectangle() : null;
|
||||
//return getFocusedRectangle();
|
||||
}
|
||||
|
||||
// private static Rectangle getFocusedRectangle() {
|
||||
// HWND hwnd = user32.GetForegroundWindow();
|
||||
// RECT rect = new User32.RECT();
|
||||
// boolean success = user32.GetWindowRect(hwnd, rect);
|
||||
// return success ? rect.toRectangle() : null;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public List<Region> getWindows(App app) {
|
||||
app = get(app);
|
||||
List<WindowInfo> windows = getWindowsForPid(app.getPID());
|
||||
|
||||
List<Region> regions = new ArrayList<>();
|
||||
|
||||
for (WindowInfo w : windows) {
|
||||
regions.add(Region.create(getRectangle(w.getHwnd(), 0)));
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="04 process info">
|
||||
public static final class ProcessInfo {
|
||||
private int pid;
|
||||
private String imageName;
|
||||
|
||||
public ProcessInfo(
|
||||
final int pid,
|
||||
final String imageName) {
|
||||
this.pid = pid;
|
||||
this.imageName = imageName;
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public String getImageName() {
|
||||
return imageName;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<ProcessInfo> allProcesses() {
|
||||
List<ProcessInfo> processList = new ArrayList<ProcessInfo>();
|
||||
|
||||
HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(
|
||||
Tlhelp32.TH32CS_SNAPPROCESS, new DWORD(0));
|
||||
|
||||
try {
|
||||
Tlhelp32.PROCESSENTRY32.ByReference pe
|
||||
= new Tlhelp32.PROCESSENTRY32.ByReference();
|
||||
for (boolean more = Kernel32.INSTANCE.Process32First(snapshot, pe);
|
||||
more;
|
||||
more = Kernel32.INSTANCE.Process32Next(snapshot, pe)) {
|
||||
int pid = pe.th32ProcessID.intValue();
|
||||
String name = getProcessImageName(pe.th32ProcessID.intValue());
|
||||
if (null == name) {
|
||||
continue;
|
||||
}
|
||||
processList.add(new ProcessInfo(pid, name));
|
||||
}
|
||||
return processList;
|
||||
} finally {
|
||||
Kernel32.INSTANCE.CloseHandle(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getProcessImageName(int pid) {
|
||||
HANDLE hProcess = Kernel32.INSTANCE.OpenProcess(
|
||||
0x1000,
|
||||
false,
|
||||
pid);
|
||||
|
||||
if (hProcess != null) {
|
||||
try {
|
||||
char[] imageNameChars = new char[1024];
|
||||
IntByReference imageNameLen
|
||||
= new IntByReference(imageNameChars.length);
|
||||
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, 0, imageNameChars, imageNameLen)) {
|
||||
String name = FilenameUtils.getName(new String(imageNameChars, 0, imageNameLen.getValue()));
|
||||
return name;
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
Kernel32.INSTANCE.CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static App getTaskByName(App app) {
|
||||
if (app.isWindow()) {
|
||||
return getTaskByWindow(app);
|
||||
} else {
|
||||
new File(app.getExec()).getName();
|
||||
String appName = app.getToken().isEmpty() ? new File(app.getExec()).getName() : app.getToken();
|
||||
|
||||
app.log("Win:getTaskByName: %s", appName);
|
||||
List<ProcessInfo> processes = allProcesses();
|
||||
|
||||
for (ProcessInfo p : processes) {
|
||||
if (p.getImageName() != null && p.getImageName().toLowerCase().equals(appName.toLowerCase())) {
|
||||
app.setPID(p.getPid());
|
||||
app.setWindow(getTopWindowTitle(p.getPid()));
|
||||
return app;
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
private static App getTaskByPID(App app) {
|
||||
if (!app.isValid()) {
|
||||
return app;
|
||||
}
|
||||
|
||||
app.log("Win:getTaskByPID: %s", app.getPID());
|
||||
List<ProcessInfo> processes = allProcesses();
|
||||
|
||||
for (ProcessInfo p : processes) {
|
||||
if (p.getPid() == app.getPID()) {
|
||||
app.setWindow(getTopWindowTitle(p.getPid()));
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
app.reset();
|
||||
return app;
|
||||
}
|
||||
|
||||
private static App getTaskByWindow(App app) {
|
||||
app.log("Win:getTaskByWindow: %s", app.getName());
|
||||
String title = app.getName();
|
||||
|
||||
List<WindowInfo> windows = allWindows();
|
||||
|
||||
for (WindowInfo window : windows) {
|
||||
String windowTitle = window.getTitle();
|
||||
|
||||
if (windowTitle != null && windowTitle.contains(title)) {
|
||||
int pid = window.getPid();
|
||||
app.setPID(pid);
|
||||
app.setWindow(windowTitle);
|
||||
List<ProcessInfo> processes = allProcesses();
|
||||
for (ProcessInfo pInfo : processes) {
|
||||
if (pid == pInfo.getPid()) {
|
||||
app.setNameGiven(app.getName());
|
||||
app.setName(pInfo.getImageName());
|
||||
}
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="05 window info">
|
||||
public static final class WindowInfo {
|
||||
public HWND hwnd;
|
||||
public int pid;
|
||||
public String title;
|
||||
|
||||
public WindowInfo(HWND hwnd, int pid, String title) {
|
||||
super();
|
||||
this.hwnd = hwnd;
|
||||
this.pid = pid;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public HWND getHwnd() {
|
||||
return hwnd;
|
||||
}
|
||||
|
||||
public int getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<WindowInfo> allWindows() {
|
||||
/* Initialize the empty window list. */
|
||||
final List<WindowInfo> windows = new ArrayList<>();
|
||||
|
||||
/* Enumerate all of the windows and add all of the one for the
|
||||
* given process id to our list. */
|
||||
boolean result = user32.EnumWindows(
|
||||
new WinUser.WNDENUMPROC() {
|
||||
public boolean callback(
|
||||
final HWND hwnd, final Pointer data) {
|
||||
|
||||
if (user32.IsWindowVisible(hwnd)) {
|
||||
IntByReference windowPid = new IntByReference();
|
||||
user32.GetWindowThreadProcessId(hwnd, windowPid);
|
||||
|
||||
String windowTitle = getWindowTitle(hwnd);
|
||||
|
||||
windows.add(new WindowInfo(hwnd, windowPid.getValue(), windowTitle));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
null);
|
||||
|
||||
/* Handle errors. */
|
||||
if (!result && Kernel32.INSTANCE.GetLastError() != 0) {
|
||||
throw new RuntimeException("Couldn't enumerate windows.");
|
||||
}
|
||||
|
||||
/* Return the window list. */
|
||||
return windows;
|
||||
}
|
||||
|
||||
private static List<WindowInfo> getWindowsForPid(int pid) {
|
||||
return allWindows().stream().filter((w) -> w.getPid() == pid).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<WindowInfo> getWindowsForName(String name) {
|
||||
return allWindows().stream().filter((w) -> {
|
||||
String imageName = getProcessImageName(w.getPid());
|
||||
|
||||
if (imageName != null && imageName.equals(name + ".exe")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String windowTitle = w.getTitle();
|
||||
|
||||
if (windowTitle != null && windowTitle.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static String getWindowTitle(HWND hWnd) {
|
||||
char[] text = new char[1024];
|
||||
int length = user32.GetWindowText(hWnd, text, 1024);
|
||||
return length > 0 ? new String(text, 0, length) : null;
|
||||
}
|
||||
|
||||
public static String getTopWindowTitle(int pid) {
|
||||
List<WindowInfo> windows = getWindowsForPid(pid);
|
||||
if (!windows.isEmpty()) {
|
||||
return getWindowsForPid(pid).get(0).getTitle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static Rectangle getRectangle(HWND hwnd, int winNum) {
|
||||
RECT rect = new User32.RECT();
|
||||
boolean success = user32.GetWindowRect(hwnd, rect);
|
||||
return success ? rect.toRectangle() : null;
|
||||
}
|
||||
|
||||
private static HWND getHwnd(String appName, int winNum) {
|
||||
List<WindowInfo> windows = getWindowsForName(appName);
|
||||
|
||||
if (windows.size() > winNum) {
|
||||
return windows.get(winNum).getHwnd();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HWND getHwnd(int pid, int winNum) {
|
||||
List<WindowInfo> windows = getWindowsForPid(pid);
|
||||
if (windows.size() > winNum) {
|
||||
return windows.get(winNum).getHwnd();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="08 lock state">
|
||||
// https://msdn.microsoft.com/pt-br/library/windows/desktop/dd375731
|
||||
// VK_NUM_LOCK 0x90
|
||||
// VK_SCROLL 0x91
|
||||
// VK_CAPITAL 0x14
|
||||
static final SXUser32 sxuser32 = SXUser32.INSTANCE;
|
||||
|
||||
public static int isNumLockOn() {
|
||||
int winNumLock = 0x90;
|
||||
return sxuser32.GetKeyState(winNumLock);
|
||||
}
|
||||
|
||||
public static int isScrollLockOn() {
|
||||
int winScrollLock = 0x91;
|
||||
return sxuser32.GetKeyState(winScrollLock);
|
||||
}
|
||||
|
||||
public static int isCapsLockOn() {
|
||||
int winCapsLock = 0x14;
|
||||
return sxuser32.GetKeyState(winCapsLock);
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
//<editor-fold desc="06 access env">
|
||||
static final int BUFFERSIZE = 32 * 1024 - 1;
|
||||
static final Kernel32 kernel32 = Kernel32.INSTANCE;
|
||||
|
||||
public static String getEnv(String envKey) {
|
||||
char[] retChar = new char[BUFFERSIZE];
|
||||
String envVal = null;
|
||||
int retInt = kernel32.GetEnvironmentVariable(envKey, retChar, BUFFERSIZE);
|
||||
if (retInt > 0) {
|
||||
envVal = new String(Arrays.copyOfRange(retChar, 0, retInt));
|
||||
}
|
||||
return envVal;
|
||||
}
|
||||
|
||||
public static String setEnv(String envKey, String envVal) {
|
||||
boolean retOK = kernel32.SetEnvironmentVariable(envKey, envVal);
|
||||
if (retOK) {
|
||||
return getEnv(envKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//</editor-fold>
|
||||
|
||||
public class WinUtil extends GenericOsUtil {
|
||||
|
||||
static final SXUser32 user32 = SXUser32.INSTANCE;
|
||||
|
||||
private static final class WinWindow implements OsWindow {
|
||||
private HWND hWnd;
|
||||
|
||||
public WinWindow(HWND hWnd) {
|
||||
this.hWnd = hWnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsProcess getProcess() {
|
||||
IntByReference pid = new IntByReference();
|
||||
user32.GetWindowThreadProcessId(hWnd, pid);
|
||||
|
||||
Optional<ProcessHandle> handle = ProcessHandle.of(pid.getValue());
|
||||
|
||||
if (handle.isPresent()) {
|
||||
return new GenericOsProcess(ProcessHandle.of(pid.getValue()).get());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
char[] text = new char[1024];
|
||||
int length = user32.GetWindowText(hWnd, text, 1024);
|
||||
return length > 0 ? new String(text, 0, length) : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
RECT rect = new User32.RECT();
|
||||
boolean success = user32.GetWindowRect(hWnd, rect);
|
||||
return success ? rect.toRectangle() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean focus() {
|
||||
WinUser.WINDOWPLACEMENT lpwndpl = new WinUser.WINDOWPLACEMENT();
|
||||
|
||||
user32.GetWindowPlacement(hWnd, lpwndpl);
|
||||
|
||||
if (lpwndpl.showCmd == WinUser.SW_SHOWMINIMIZED || lpwndpl.showCmd == WinUser.SW_MINIMIZE) {
|
||||
user32.ShowWindow(hWnd, WinUser.SW_RESTORE);
|
||||
}
|
||||
|
||||
boolean success = user32.SetForegroundWindow(hWnd);
|
||||
|
||||
if (success) {
|
||||
return (user32.SetFocus(hWnd) != null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean minimize() {
|
||||
return user32.ShowWindow(hWnd, WinUser.SW_MINIMIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean maximize() {
|
||||
return user32.ShowWindow(hWnd, WinUser.SW_MAXIMIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore() {
|
||||
return user32.ShowWindow(hWnd, WinUser.SW_RESTORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null && other instanceof WinWindow && this.hWnd.equals(((WinWindow) other).hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsWindow> findWindows(String title) {
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
return allWindows().stream().filter((w) -> w.getTitle().contains(title)).collect(Collectors.toList());
|
||||
}
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsWindow> getWindows(OsProcess process) {
|
||||
if (process != null) {
|
||||
return allWindows().stream().filter((w) -> process.equals(w.getProcess())).collect(Collectors.toList());
|
||||
}
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OsWindow getFocusedWindow() {
|
||||
HWND hWnd = user32.GetForegroundWindow();
|
||||
return new WinWindow(hWnd);
|
||||
}
|
||||
|
||||
private List<OsWindow> allWindows() {
|
||||
/* Initialize the empty window list. */
|
||||
final List<OsWindow> windows = new ArrayList<>();
|
||||
|
||||
boolean result = user32.EnumWindows(new WinUser.WNDENUMPROC() {
|
||||
public boolean callback(final HWND hWnd, final Pointer data) {
|
||||
// Only visible and top level. Ensures that top level window is at index 0
|
||||
if (user32.IsWindowVisible(hWnd) && user32.GetWindow(hWnd, new DWORD(WinUser.GW_OWNER)) == null) {
|
||||
windows.add(new WinWindow(hWnd));
|
||||
|
||||
// get child windows as well
|
||||
user32.EnumChildWindows(hWnd, new WinUser.WNDENUMPROC() {
|
||||
public boolean callback(final HWND hWnd, final Pointer data) {
|
||||
if (user32.IsWindowVisible(hWnd)) {
|
||||
windows.add(new WinWindow(hWnd));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}, null);
|
||||
|
||||
/* Handle errors. */
|
||||
if (!result && Kernel32.INSTANCE.GetLastError() != 0) {
|
||||
throw new RuntimeException("Couldn't enumerate windows.");
|
||||
}
|
||||
|
||||
return windows;
|
||||
}
|
||||
|
||||
// https://msdn.microsoft.com/pt-br/library/windows/desktop/dd375731
|
||||
// VK_NUM_LOCK 0x90
|
||||
// VK_SCROLL 0x91
|
||||
// VK_CAPITAL 0x14
|
||||
|
||||
public static int isNumLockOn() {
|
||||
int winNumLock = 0x90;
|
||||
return user32.GetKeyState(winNumLock);
|
||||
}
|
||||
|
||||
public static int isScrollLockOn() {
|
||||
int winScrollLock = 0x91;
|
||||
return user32.GetKeyState(winScrollLock);
|
||||
}
|
||||
|
||||
public static int isCapsLockOn() {
|
||||
int winCapsLock = 0x14;
|
||||
return user32.GetKeyState(winCapsLock);
|
||||
}
|
||||
|
||||
static final int BUFFERSIZE = 32 * 1024 - 1;
|
||||
static final Kernel32 kernel32 = Kernel32.INSTANCE;
|
||||
|
||||
public static String getEnv(String envKey) {
|
||||
char[] retChar = new char[BUFFERSIZE];
|
||||
String envVal = null;
|
||||
int retInt = kernel32.GetEnvironmentVariable(envKey, retChar, BUFFERSIZE);
|
||||
if (retInt > 0) {
|
||||
envVal = new String(Arrays.copyOfRange(retChar, 0, retInt));
|
||||
}
|
||||
return envVal;
|
||||
}
|
||||
|
||||
public static String setEnv(String envKey, String envVal) {
|
||||
boolean retOK = kernel32.SetEnvironmentVariable(envKey, envVal);
|
||||
if (retOK) {
|
||||
return getEnv(envKey);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
128
API/src/main/java/org/sikuli/natives/mac/jna/CoreGraphics.java
Normal file
128
API/src/main/java/org/sikuli/natives/mac/jna/CoreGraphics.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package org.sikuli.natives.mac.jna;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.Structure;
|
||||
import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef;
|
||||
import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;
|
||||
|
||||
/**
|
||||
* JNA bindings to CoreGraphics *
|
||||
*/
|
||||
|
||||
public interface CoreGraphics extends Library {
|
||||
|
||||
/**
|
||||
* Window number key
|
||||
*/
|
||||
public static final CFStringRef kCGWindowNumber = CFStringRef.createCFString("kCGWindowNumber");
|
||||
|
||||
/**
|
||||
* Window owner pid key
|
||||
*/
|
||||
public static final CFStringRef kCGWindowOwnerPID = CFStringRef.createCFString("kCGWindowOwnerPID");
|
||||
|
||||
/**
|
||||
* Window name key
|
||||
*/
|
||||
public static final CFStringRef kCGWindowName = CFStringRef.createCFString("kCGWindowName");
|
||||
|
||||
/**
|
||||
* Window owner name key
|
||||
*/
|
||||
public static final CFStringRef kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName");
|
||||
|
||||
/**
|
||||
* Window bounds key
|
||||
*/
|
||||
public static final CFStringRef kCGWindowBounds = CFStringRef.createCFString("kCGWindowBounds");
|
||||
|
||||
/**
|
||||
* List all windows in this user session, including both on- and off-screen
|
||||
* windows. The parameter `relativeToWindow' should be `kCGNullWindowID'.
|
||||
*/
|
||||
public static final int kCGWindowListOptionAll = 0;
|
||||
|
||||
/*
|
||||
* List all on-screen windows in this user session, ordered from front to back.
|
||||
* The parameter `relativeToWindow' should be `kCGNullWindowID'.
|
||||
*/
|
||||
public static final int kCGWindowListOptionOnScreenOnly = (1 << 0);
|
||||
|
||||
/*
|
||||
* List all on-screen windows above the window specified by `relativeToWindow',
|
||||
* ordered from front to back.
|
||||
*/
|
||||
public static final int kCGWindowListOptionOnScreenAboveWindow = (1 << 1);
|
||||
|
||||
/*
|
||||
* List all on-screen windows below the window specified by `relativeToWindow',
|
||||
* ordered from front to back.
|
||||
*/
|
||||
public static final int kCGWindowListOptionOnScreenBelowWindow = (1 << 2);
|
||||
|
||||
/*
|
||||
* Include the window specified by `relativeToWindow' in any list, effectively
|
||||
* creating `at-or-above' or `at-or-below' lists.
|
||||
*/
|
||||
public static final int kCGWindowListOptionIncludingWindow = (1 << 3);
|
||||
|
||||
/* Exclude any windows from the list that are elements of the desktop. */
|
||||
public static final int kCGWindowListExcludeDesktopElements = (1 << 4);
|
||||
|
||||
CoreGraphics INSTANCE = Native.load("CoreGraphics", CoreGraphics.class);
|
||||
|
||||
CFArrayRef CGWindowListCopyWindowInfo(int option, int relativeToWindow);
|
||||
boolean CGRectMakeWithDictionaryRepresentation(Pointer dict, CGRectRef rect);
|
||||
|
||||
class CGPoint extends Structure {
|
||||
public double x;
|
||||
public double y;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("x", "y");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CGSize extends Structure {
|
||||
public double width;
|
||||
public double height;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("width", "height");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CGRect extends Structure implements Structure.ByValue {
|
||||
public static class CGRectByValue extends CGRect {
|
||||
}
|
||||
|
||||
public CGPoint origin;
|
||||
public CGSize size;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("origin", "size");
|
||||
}
|
||||
}
|
||||
|
||||
class CGRectRef extends Structure implements Structure.ByReference {
|
||||
public static class CGRectByReference extends CGRect {
|
||||
}
|
||||
|
||||
public CGPoint origin;
|
||||
public CGSize size;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
return Arrays.asList("origin", "size");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.sikuli.natives.mac.jna;
|
||||
|
||||
import org.rococoa.ObjCClass;
|
||||
import org.rococoa.ObjCObject;
|
||||
import org.rococoa.Rococoa;
|
||||
import org.rococoa.cocoa.foundation.NSArray;
|
||||
|
||||
/**
|
||||
* JNA binding for NSRunningApplication
|
||||
*
|
||||
* @author mbalmer
|
||||
*
|
||||
*/
|
||||
public interface NSRunningApplication extends ObjCObject {
|
||||
|
||||
public static final _Class CLASS = Rococoa.createClass("NSRunningApplication", _Class.class);
|
||||
|
||||
|
||||
public interface _Class extends ObjCClass {
|
||||
/**
|
||||
* Finds all applications with the given bundle name
|
||||
*
|
||||
* @param bundleIdentifier
|
||||
* @return
|
||||
*/
|
||||
NSArray runningApplicationsWithBundleIdentifier(String bundleIdentifier);
|
||||
|
||||
/**
|
||||
* Finds all applicaitons with the given pid
|
||||
*
|
||||
* @param pid
|
||||
* @return
|
||||
*/
|
||||
NSRunningApplication runningApplicationWithProcessIdentifier(int pid);
|
||||
}
|
||||
|
||||
public static interface NSApplicationActivationOptions {
|
||||
/** all of the application's windows are brought forward. */
|
||||
public static final int NSApplicationActivateAllWindows = 1 << 0;
|
||||
/** the application is activated regardless of the currently active app, potentially stealing focus from the user */
|
||||
public static final int NSApplicationActivateIgnoringOtherApps = 1 << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this running applications bundle identifier
|
||||
*/
|
||||
String bundleIdentifier();
|
||||
|
||||
/**
|
||||
* @return this running applications PID
|
||||
*/
|
||||
int processIdentifier();
|
||||
|
||||
/**
|
||||
* Activates this running application.
|
||||
*
|
||||
* @param options
|
||||
* @return
|
||||
*/
|
||||
boolean activateWithOptions(int options);
|
||||
|
||||
/**
|
||||
* Checks if this running application is currently active.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isActive();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,8 +46,8 @@ public abstract class AbstractScriptRunner implements IScriptRunner {
|
||||
|
||||
private boolean running = false;
|
||||
|
||||
PrintStream redirectedStdout;
|
||||
PrintStream redirectedStderr;
|
||||
private PrintStream redirectedStdout;
|
||||
private PrintStream redirectedStderr;
|
||||
|
||||
private static volatile Thread worker = null;
|
||||
private static final ScheduledExecutorService TIMEOUT_EXECUTOR = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
@@ -6,51 +6,24 @@ package org.sikuli.script.runners;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.sikuli.basics.FileManager;
|
||||
import org.sikuli.script.support.IScriptRunner;
|
||||
import org.sikuli.script.support.RunTime;
|
||||
|
||||
public class AppleScriptRunner extends AbstractLocalFileScriptRunner {
|
||||
public class AppleScriptRunner extends ProcessRunner {
|
||||
|
||||
public static final String NAME = "AppleScript";
|
||||
public static final String TYPE = "text/applescript";
|
||||
public static final String[] EXTENSIONS = new String[] {"script"};
|
||||
|
||||
private static final int LVL = 3;
|
||||
private static final RunTime RUN_TIME = RunTime.get();
|
||||
public static final String[] EXTENSIONS = new String[] {"scpt", "scptd", "applescript"};
|
||||
|
||||
@Override
|
||||
protected int doEvalScript(String script, IScriptRunner.Options options) {
|
||||
String osascriptShebang = "#!/usr/bin/osascript\n";
|
||||
script = osascriptShebang + script;
|
||||
File aFile = FileManager.createTempFile("script");
|
||||
aFile.setExecutable(true);
|
||||
FileManager.writeStringToFile(script, aFile);
|
||||
|
||||
int retcode = runScript(aFile.getAbsolutePath(), null, options);
|
||||
|
||||
if (retcode != 0) {
|
||||
if (options != null && options.isSilent()) {
|
||||
log(LVL, "AppleScript:\n%s\nreturned:\n%s", script, RUN_TIME.getLastCommandResult());
|
||||
} else {
|
||||
log(-1, "AppleScript:\n%s\nreturned:\n%s", script, RUN_TIME.getLastCommandResult());
|
||||
}
|
||||
}
|
||||
return retcode;
|
||||
return super.doRunScript("osascript", new String[]{"-e", script}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doRunScript(String scriptFile, String[] scriptArgs, IScriptRunner.Options options) {
|
||||
String prefix = options != null && options.isSilent() ? "!" : "";
|
||||
|
||||
String retVal = RUN_TIME.runcmd(new String[]{prefix + new File(scriptFile).getAbsolutePath()});
|
||||
String[] parts = retVal.split("\n");
|
||||
int retcode = -1;
|
||||
try {
|
||||
retcode = Integer.parseInt(parts[0]);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
return retcode;
|
||||
String path = new File(scriptFile).getAbsolutePath();
|
||||
return super.doRunScript("osascript", new String[] {path}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,49 +5,31 @@ package org.sikuli.script.runners;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.sikuli.basics.FileManager;
|
||||
import org.sikuli.script.support.IScriptRunner;
|
||||
import org.sikuli.script.support.RunTime;
|
||||
|
||||
public class PowershellRunner extends AbstractLocalFileScriptRunner {
|
||||
public class PowershellRunner extends ProcessRunner {
|
||||
|
||||
public static final String NAME = "PowerShell";
|
||||
public static final String TYPE = "text/powershell";
|
||||
public static final String[] EXTENSIONS = new String[] {"ps1"};
|
||||
|
||||
private static final RunTime RUN_TIME = RunTime.get();
|
||||
|
||||
@Override
|
||||
protected int doEvalScript(String script, IScriptRunner.Options options) {
|
||||
File aFile = FileManager.createTempFile("ps1");
|
||||
FileManager.writeStringToFile(script, aFile);
|
||||
return runScript(aFile.getAbsolutePath(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doRunScript(String scriptFile, String[] scriptArgs, IScriptRunner.Options options) {
|
||||
File fScriptFile = new File(scriptFile);
|
||||
|
||||
String[] psDirect = new String[]{
|
||||
"powershell.exe", "-ExecutionPolicy", "UnRestricted",
|
||||
"-NonInteractive", "-NoLogo", "-NoProfile", "-WindowStyle", "Hidden",
|
||||
"-File", fScriptFile.getAbsolutePath()
|
||||
// String[] psDirect = new String[]{
|
||||
// "powershell.exe", "-ExecutionPolicy", "UnRestricted",
|
||||
// "-NonInteractive", "-NoLogo", "-NoProfile", "-WindowStyle", "Hidden",
|
||||
// "-File", fScriptFile.getAbsolutePath()
|
||||
// };
|
||||
|
||||
String cmd = "cmd.exe";
|
||||
String[] args = new String[] {
|
||||
"/S", "/C", "type \"" + fScriptFile.getAbsolutePath() + "\" | powershell -noprofile -"
|
||||
};
|
||||
String[] psCmdType = new String[]{
|
||||
"cmd.exe", "/S", "/C",
|
||||
"type " + fScriptFile.getAbsolutePath() + " | powershell -noprofile -"
|
||||
};
|
||||
String retVal = RUN_TIME.runcmd(psCmdType);
|
||||
String[] parts = retVal.split("\\s");
|
||||
int retcode = -1;
|
||||
try {
|
||||
retcode = Integer.parseInt(parts[0]);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
if (retcode != 0) {
|
||||
log(-1, "PowerShell:\n%s\nreturned:\n%s", fScriptFile, RUN_TIME.getLastCommandResult());
|
||||
}
|
||||
return retcode;
|
||||
|
||||
return super.doRunScript(cmd, args, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,309 +4,290 @@
|
||||
|
||||
package org.sikuli.script.runners;
|
||||
|
||||
import org.sikuli.script.support.RunTime;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProcessRunner extends AbstractScriptRunner{
|
||||
import org.apache.commons.exec.StreamPumper;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.sikuli.basics.Debug;
|
||||
import org.sikuli.basics.FileManager;
|
||||
import org.sikuli.script.support.IScriptRunner;
|
||||
|
||||
public static final String NAME = "Process";
|
||||
public static final String TYPE = "text/application";
|
||||
public static final String[] EXTENSIONS = new String[0];
|
||||
public class ProcessRunner extends AbstractLocalFileScriptRunner {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
public static final String NAME = "Process";
|
||||
public static final String TYPE = "text/application";
|
||||
public static final String[] EXTENSIONS = new String[0];
|
||||
|
||||
@Override
|
||||
public String[] getExtensions() {
|
||||
return EXTENSIONS;
|
||||
}
|
||||
private Process process;
|
||||
private PrintStream stdOut;
|
||||
private PrintStream stdErr;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
private static void p(String form, Object... args) {
|
||||
System.out.println(String.format(form, args));
|
||||
}
|
||||
@Override
|
||||
public String[] getExtensions() {
|
||||
return EXTENSIONS;
|
||||
}
|
||||
|
||||
public static String runCommand(String... args) throws IOException, InterruptedException {
|
||||
String result = "";
|
||||
String work = null;
|
||||
if (args.length > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("work=")) {
|
||||
work = arg.substring(5);
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("javahome=")) {
|
||||
processEnv.put("JAVA_HOME", arg.substring(9));
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("?")) {
|
||||
final String fName = arg.substring(1);
|
||||
String folder = null;
|
||||
if (null == work) {
|
||||
folder = System.getProperty("user.dir");
|
||||
} else {
|
||||
folder = work;
|
||||
}
|
||||
String[] fList = new File(folder).list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
if (name.startsWith(fName)) return true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (fList.length > 0) {
|
||||
arg = fList[0];
|
||||
}
|
||||
}
|
||||
cmd.add(arg);
|
||||
}
|
||||
app.directory(work == null ? null : new File(work));
|
||||
app.redirectErrorStream(true);
|
||||
app.command(cmd);
|
||||
Process process = app.start();
|
||||
InputStreamReader reader = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader processOut = new BufferedReader(reader);
|
||||
String line = processOut.readLine();
|
||||
while (null != line) {
|
||||
result += line + "\n";
|
||||
line = processOut.readLine();
|
||||
}
|
||||
process.waitFor();
|
||||
int exitValue = process.exitValue();
|
||||
if (exitValue > 0) {
|
||||
result += "error";
|
||||
} else {
|
||||
result = "success\n" + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static String run(String... args) {
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
for (String arg : args) {
|
||||
cmd.add(arg);
|
||||
}
|
||||
return run(cmd);
|
||||
}
|
||||
@Override
|
||||
protected int doEvalScript(String script, IScriptRunner.Options options) {
|
||||
String extension = "script";
|
||||
String[] extensions = getExtensions();
|
||||
|
||||
public static String run(List<String> cmd) {
|
||||
int exitValue = 0;
|
||||
String stdout = "";
|
||||
if (cmd.size() > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
app.command(cmd);
|
||||
app.redirectErrorStream(true);
|
||||
Process process = null;
|
||||
try {
|
||||
process = app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (process != null) {
|
||||
InputStreamReader reader = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader processOut = new BufferedReader(reader);
|
||||
String line = processOut.readLine();
|
||||
while (null != line) {
|
||||
//System.out.println(line);
|
||||
stdout += line;
|
||||
line = processOut.readLine();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
p("[Error] ProcessRunner: read: %s", e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (process != null) {
|
||||
process.waitFor();
|
||||
exitValue = process.exitValue();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
p("[Error] ProcessRunner: waitFor: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
return "" + exitValue + "\n" + stdout;
|
||||
}
|
||||
if (extensions.length > 0) {
|
||||
extension = extensions[0];
|
||||
}
|
||||
|
||||
public static void detach(String... args) {
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
for (String arg : args) {
|
||||
cmd.add(arg);
|
||||
}
|
||||
detach(cmd);
|
||||
}
|
||||
File file = FileManager.createTempFile(extension);
|
||||
FileManager.writeStringToFile(script, file);
|
||||
return runScript(file.getAbsolutePath(), null, options);
|
||||
}
|
||||
|
||||
public static void detach(List<String> cmd) {
|
||||
if (cmd.size() > 0) {
|
||||
String line = "";
|
||||
boolean shouldPrint = false;
|
||||
for (String item : cmd) {
|
||||
line += " " + item.trim();
|
||||
if ("-v".equals(item.trim())) {
|
||||
shouldPrint = true;
|
||||
}
|
||||
}
|
||||
if (shouldPrint) {
|
||||
System.out.println("[DEBUG] ProcessRunner::detach:");
|
||||
System.out.println(line.trim());
|
||||
}
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
app.command(cmd);
|
||||
app.redirectErrorStream(true);
|
||||
app.redirectInput(ProcessBuilder.Redirect.INHERIT);
|
||||
app.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
try {
|
||||
app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected int doRunScript(String scriptFile, String[] scriptArgs, IScriptRunner.Options options) {
|
||||
String[] cmdArgs = ArrayUtils.addAll(new String[] { scriptFile }, scriptArgs);
|
||||
|
||||
public static int runBlocking(List<String> cmd) {
|
||||
int exitValue = 0;
|
||||
if (cmd.size() > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
app.command(cmd);
|
||||
app.redirectInput(ProcessBuilder.Redirect.INHERIT);
|
||||
app.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
Process process = null;
|
||||
try {
|
||||
process = app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
}
|
||||
int exitCode = 0;
|
||||
|
||||
try {
|
||||
if (process != null) {
|
||||
process.waitFor();
|
||||
exitValue = process.exitValue();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
p("[Error] ProcessRunner: waitFor: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
return exitValue;
|
||||
}
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(cmdArgs);
|
||||
|
||||
public static boolean closeApp(String... givenCmd) {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.addAll(Arrays.asList(givenCmd));
|
||||
return closeApp(cmd);
|
||||
}
|
||||
startStreamPumper(process.getInputStream(), stdOut, options);
|
||||
startStreamPumper(process.getErrorStream(), stdErr, options);
|
||||
|
||||
public static boolean closeApp(List<String> givenCmd) {
|
||||
RunTime runTime = RunTime.get();
|
||||
int exitValue = 0;
|
||||
if (runTime.runningWindows) {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add("cmd");
|
||||
cmd.add("/C");
|
||||
cmd.add("taskkill");
|
||||
cmd.add("/PID");
|
||||
cmd.add(givenCmd.get(0));
|
||||
cmd.add(">nul");
|
||||
cmd.add("2>&1");
|
||||
if (cmd.size() > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
app.command(cmd);
|
||||
app.redirectErrorStream(true);
|
||||
app.redirectInput(ProcessBuilder.Redirect.INHERIT);
|
||||
app.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
try {
|
||||
app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: taskkill: %s", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
exitCode = process.waitFor();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
if (!isAborted()) {
|
||||
Debug.error("%s failed: %s", getName(), e.getMessage());
|
||||
exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean startApp(String... givenCmd) {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.addAll(Arrays.asList(givenCmd));
|
||||
return startApp(cmd);
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
public static boolean startApp(List<String> givenCmd) {
|
||||
RunTime runTime = RunTime.get();
|
||||
int exitValue = 0;
|
||||
if (runTime.runningWindows) {
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add("cmd");
|
||||
cmd.add("/C");
|
||||
cmd.add("start");
|
||||
cmd.add("\"\"");
|
||||
cmd.add("/B");
|
||||
if (!givenCmd.get(1).isEmpty()) {
|
||||
cmd.add("/D");
|
||||
cmd.add("\"" + givenCmd.get(1) + "\"");
|
||||
}
|
||||
cmd.add("\"" + givenCmd.get(0) + "\"");
|
||||
if (givenCmd.size() > 2) {
|
||||
for (int np = 2; np < givenCmd.size(); np++) {
|
||||
startAppParams(cmd, givenCmd.get(np));
|
||||
}
|
||||
}
|
||||
cmd.add(">nul");
|
||||
cmd.add("2>&1");
|
||||
if (cmd.size() > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
app.command(cmd);
|
||||
app.redirectErrorStream(true);
|
||||
app.redirectInput(ProcessBuilder.Redirect.INHERIT);
|
||||
app.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
try {
|
||||
app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public boolean isAbortSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<String> startAppParams(List<String> cmd, String param) {
|
||||
String[] params = param.split(" ");
|
||||
String concatParm = "";
|
||||
for (String parm : params) {
|
||||
if (parm.startsWith("\"")) {
|
||||
concatParm = parm;
|
||||
continue;
|
||||
}
|
||||
if (!concatParm.isEmpty()) {
|
||||
if (!parm.endsWith("\"")) {
|
||||
concatParm += " " + parm;
|
||||
continue;
|
||||
}
|
||||
parm = concatParm + " " + parm;
|
||||
concatParm = "";
|
||||
}
|
||||
cmd.add(parm.trim());
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
@Override
|
||||
protected void doAbort() {
|
||||
if (process != null) {
|
||||
process.descendants().forEach((p) -> p.destroyForcibly());
|
||||
process.destroyForcibly();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doRedirect(PrintStream stdout, PrintStream stderr) {
|
||||
this.stdOut = stdout;
|
||||
this.stdErr = stderr;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startStreamPumper(InputStream in, OutputStream out, IScriptRunner.Options options) {
|
||||
if (out != null && !options.isSilent()) {
|
||||
new Thread(new StreamPumper(in, out)).start();
|
||||
} else {
|
||||
// Ensure that we read the input stream buffer.
|
||||
// Process might block otherwise as soon as the buffer is full.
|
||||
new Thread(new StreamPumper(in, OutputStream.nullOutputStream(), true)).start();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Check if rest of the file is really well placed here.
|
||||
// Hint: Most probably not :-)
|
||||
|
||||
private static void p(String form, Object... args) {
|
||||
System.out.println(String.format(form, args));
|
||||
}
|
||||
|
||||
public static String runCommand(String... args) throws IOException, InterruptedException {
|
||||
String result = "";
|
||||
String work = null;
|
||||
if (args.length > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("work=")) {
|
||||
work = arg.substring(5);
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("javahome=")) {
|
||||
processEnv.put("JAVA_HOME", arg.substring(9));
|
||||
continue;
|
||||
}
|
||||
if (arg.startsWith("?")) {
|
||||
final String fName = arg.substring(1);
|
||||
String folder = null;
|
||||
if (null == work) {
|
||||
folder = System.getProperty("user.dir");
|
||||
} else {
|
||||
folder = work;
|
||||
}
|
||||
String[] fList = new File(folder).list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
if (name.startsWith(fName))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (fList.length > 0) {
|
||||
arg = fList[0];
|
||||
}
|
||||
}
|
||||
cmd.add(arg);
|
||||
}
|
||||
app.directory(work == null ? null : new File(work));
|
||||
app.redirectErrorStream(true);
|
||||
app.command(cmd);
|
||||
Process process = app.start();
|
||||
InputStreamReader reader = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader processOut = new BufferedReader(reader);
|
||||
String line = processOut.readLine();
|
||||
while (null != line) {
|
||||
result += line + "\n";
|
||||
line = processOut.readLine();
|
||||
}
|
||||
process.waitFor();
|
||||
int exitValue = process.exitValue();
|
||||
if (exitValue > 0) {
|
||||
result += "error";
|
||||
} else {
|
||||
result = "success\n" + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String run(String... args) {
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
for (String arg : args) {
|
||||
cmd.add(arg);
|
||||
}
|
||||
return run(cmd);
|
||||
}
|
||||
|
||||
public static String run(List<String> cmd) {
|
||||
int exitValue = 0;
|
||||
String stdout = "";
|
||||
if (cmd.size() > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
app.command(cmd);
|
||||
app.redirectErrorStream(true);
|
||||
Process process = null;
|
||||
try {
|
||||
process = app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (process != null) {
|
||||
InputStreamReader reader = new InputStreamReader(process.getInputStream());
|
||||
BufferedReader processOut = new BufferedReader(reader);
|
||||
String line = processOut.readLine();
|
||||
while (null != line) {
|
||||
// System.out.println(line);
|
||||
stdout += line;
|
||||
line = processOut.readLine();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
p("[Error] ProcessRunner: read: %s", e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (process != null) {
|
||||
process.waitFor();
|
||||
exitValue = process.exitValue();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
p("[Error] ProcessRunner: waitFor: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
return "" + exitValue + "\n" + stdout;
|
||||
}
|
||||
|
||||
public static void detach(String... args) {
|
||||
List<String> cmd = new ArrayList<String>();
|
||||
for (String arg : args) {
|
||||
cmd.add(arg);
|
||||
}
|
||||
detach(cmd);
|
||||
}
|
||||
|
||||
public static void detach(List<String> cmd) {
|
||||
if (cmd.size() > 0) {
|
||||
String line = "";
|
||||
boolean shouldPrint = false;
|
||||
for (String item : cmd) {
|
||||
line += " " + item.trim();
|
||||
if ("-v".equals(item.trim())) {
|
||||
shouldPrint = true;
|
||||
}
|
||||
}
|
||||
if (shouldPrint) {
|
||||
System.out.println("[DEBUG] ProcessRunner::detach:");
|
||||
System.out.println(line.trim());
|
||||
}
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
Map<String, String> processEnv = app.environment();
|
||||
app.command(cmd);
|
||||
app.redirectErrorStream(true);
|
||||
app.redirectInput(ProcessBuilder.Redirect.INHERIT);
|
||||
app.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
try {
|
||||
app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int runBlocking(List<String> cmd) {
|
||||
int exitValue = 0;
|
||||
if (cmd.size() > 0) {
|
||||
ProcessBuilder app = new ProcessBuilder();
|
||||
app.command(cmd);
|
||||
app.redirectInput(ProcessBuilder.Redirect.INHERIT);
|
||||
app.redirectOutput(ProcessBuilder.Redirect.INHERIT);
|
||||
Process process = null;
|
||||
try {
|
||||
process = app.start();
|
||||
} catch (Exception e) {
|
||||
p("[Error] ProcessRunner: start: %s", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (process != null) {
|
||||
process.waitFor();
|
||||
exitValue = process.exitValue();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
p("[Error] ProcessRunner: waitFor: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
return exitValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
API/src/main/resources/darwin/librococoa.dylib
Normal file
BIN
API/src/main/resources/darwin/librococoa.dylib
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,2 +1 @@
|
||||
libMacUtil.dylib
|
||||
libopencv_java.dylib
|
||||
|
||||
Reference in New Issue
Block a user