Merge pull request #418 from SKOORAG/appClassRefactoring

App class refactoring
This commit is contained in:
Raimund Hocke
2021-04-25 08:27:08 +02:00
committed by GitHub
17 changed files with 1999 additions and 2411 deletions

View File

@@ -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>

View 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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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()));
}

View File

@@ -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;
}
}

View 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");
}
}
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

Binary file not shown.

View File

@@ -1,2 +1 @@
libMacUtil.dylib
libopencv_java.dylib