org.sikuli.natives.LinuxUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sikulixapi Show documentation
Show all versions of sikulixapi Show documentation
... for visual testing and automation
/*
* Copyright (c) 2010-2016, Sikuli.org, sikulix.com
* Released under the MIT License.
*
*/
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.sikuli.basics.Debug;
import org.sikuli.script.App;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class LinuxUtil implements OSUtil {
private static boolean wmctrlAvail = true;
private static boolean xdoToolAvail = true;
@Override
public void checkFeatureAvailability() {
List commands = Arrays.asList(
CommandLine.parse("wmctrl -m"),
CommandLine.parse("xdotool version")
);
for (CommandLine cmd : commands) {
String executable = cmd.toStrings()[0];
try {
DefaultExecutor executor = new DefaultExecutor();
// other return values throw exception
executor.setExitValue(0);
//suppress system output
executor.setStreamHandler(new PumpStreamHandler(null));
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",
executable, e.toString());
} catch (Exception e) {
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);
}
}
}
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;
}
@Override
public App.AppEntry getApp(int appPID, String appName) {
return new App.AppEntry(appName, "" + appPID, "", "", "");
}
@Override
public int isRunning(App.AppEntry app) {
return -1;
}
@Override
public 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 0;
}
}
@Override
public int open(App.AppEntry app) {
return open(app.execName);
}
@Override
public int switchto(String appName, int winNum) {
int windowPID = findWindowPID(appName, winNum);
if (windowPID > 1) {
return switchto(windowPID, winNum);
}
System.err.println("[error] switchApp: could not identify process with search name '" + appName + "'");
return -1;
}
@Override
public int switchto(String appName) {
return switchto(appName, 0);
}
@Override
public int switchto(App.AppEntry app, int num) {
if (app.pid > 0) {
return switchto(app.pid, num);
}
return switchto(app.execName, num);
}
@Override
public 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;
}
}
@Override
public int close(App.AppEntry app) {
if (app.pid > 0) {
return close(app.pid);
}
return close(app.execName);
}
@Override
public Map getApps(String name) {
return null;
}
@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 = 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 Rectangle getWindow(String appName) {
return getWindow(appName, 0);
}
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+");
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[9].toLowerCase();
if (appName.equals(winLineName)) {
ok = true;
}
if (!ok && winLine[7].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
*/
protected int findWindowPID(String appName, int winNum) {
String[] window = findWindow(appName, winNum, SearchType.APP_NAME);
if (window != null && window.length > 1) {
return Integer.parseInt(window[2]);
}
return -1;
}
@Override
public Rectangle getWindow(String appName, int winNum) {
return findRegion(appName, winNum, SearchType.APP_NAME);
}
@Override
public Rectangle getWindow(int pid) {
return getWindow(pid, 0);
}
@Override
public Rectangle getWindow(int pid, int winNum) {
return findRegion("" + pid, winNum, SearchType.PID);
}
@Override
public 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;
}
}
@Override
public 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) {
System.err.println("[error] switchApp: window of PID '" + pid + "' couldn't be found!");
return -1;
}
try {
// execute wmctrl with hex, e.g. 'wmctrl -ia 0x00000'
CommandExecutorHelper.execute("wmctrl -ia " + winLine[0], 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 void bringWindowToFront(Window win, boolean ignoreMouse) {
}
private enum SearchType {
APP_NAME,
WINDOW_ID,
PID
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy