0

I'd like to know whether a certain application is in focus in Linux. Say it is Google Chrome. To do so, I wrote a bash on-liner which does it correctly.

xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)

When this command is run in terminal, it will print the id of the terminal itself. To avoid that, run the following command and select Chrome in the three seconds time span.

sleep 3; xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)

The problem is that when I run the above-mentioned one-liner from Java, it seems to always print nothing. Here's my code:

String cmd = "xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)";
Process p = Runtime.getRuntime().exec(cmd);
String result = getCommandResult(p.getInputStream());

private static String getCommandResult(InputStream stream) throws IOException {

    StringBuilder sb = new StringBuilder();
    try (InputStreamReader isr = new InputStreamReader(stream);
         BufferedReader in = new BufferedReader(isr)) {

        String line;
        while ((line = in.readLine()) != null) {
            sb.append(line);
        }
    }
    return sb.toString().trim();
}

I'm open to different solutions to resolving this problem.

4
  • Why does your question contain parts of additional notes that look like a copy&paste from this answer: How to get window id from xdotool Window Stack ? Commented Sep 15, 2019 at 13:21
  • @Tom, I'm not a native speaker so to be understood I copied and modified Note that running that command in the terminal will always return the idof the terminal windo as it is an active window. In order to get the id of another window try:. Anything else, especially the code, is mine. Is this a problem for you? (I rewrote the text using my own words). Commented Sep 15, 2019 at 13:29
  • See also When Runtime.exec() won't for many good tips on creating and handling a process correctly. Then ignore it refers to exec and use a ProcessBuilder to create the process. Also break a String arg into String[] args to account for things like paths containing space characters. Commented Sep 15, 2019 at 15:07
  • Or nearer at hand stackoverflow.com/questions/31776546/… and maybe stackoverflow.com/questions/5928225/… and stackoverflow.com/questions/2088917/… Commented Sep 15, 2019 at 20:25

3 Answers 3

1
+100

As barti_ddu said, this is not working because of the pipe in the command. You can workaround this by creating one sh process with your command passed as the argument:

    String cmd = "xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)";
    Process p = new ProcessBuilder("sh", "-c", cmd).start();
    String result = getCommandResult(p.getInputStream());
Sign up to request clarification or add additional context in comments.

Comments

1

If You are using pipe redirection, then You either have to invoke the shell, or redirect the output from one program to another yourself.

However, IMHO, You do not need grep at all, just:

  1. Enumerate windows of certain class.
  2. Get the active window id and check if the active window list contains it.

Example (q&d, resource/error handling omitted):

private static List<String> exec(String... args) throws IOException {
    List<String> result = new ArrayList<>();
    String line;

    BufferedReader reader = new BufferedReader(
            new InputStreamReader(
                    new ProcessBuilder()
                            .command(args)
                            .start()
                            .getInputStream()));

    while ((line = reader.readLine()) != null) {
        result.add(line);
    }

    return result;
}

public static void main(String[] args) {
    try {
        Thread.sleep(3000);

        String windowId = exec("xdotool", "getactivewindow").get(0);
        System.out.println(windowId);

        List<String> windowList = exec("xdotool", "search", "--name", "--class", "google-chrome");
        System.out.println(windowList.contains(windowId));
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }

Comments

0

Why are you hard coding the command into your class? Rather put your command in a shell script and call that instead. Then you have the flexibility to change the command without having to re-compile.

Process proc = Runtime.getRuntime().exec("./opt/scripts/myscript.sh");

... depending on your application, it would potentially be even better to pass in the shell script as parameter.

private void runCommandLine(String script) {
    try {
        Process proc = Runtime.getRuntime().exec(script);
        proc.waitFor();
        int character;
        while((character = proc.getInputStream().read()) != -1)  {
            System.out.write(character);
        }
        while((character = proc.getErrorStream().read()) != -1) {
            System.err.write(character);
        }
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }
}

EDIT:

If you goal is to figure out what window is currently in focus, then instead of trying to execute something via the command line, you could just use the JNA (Java Native Access) libraries to potentially do this.

Find out what application (window) is in focus in Java

Java Native Access (Github)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.