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.

menteith
  • 596
  • 14
  • 51
  • 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](//unix.stackexchange.com/a/154551) ? – Tom Sep 15 '19 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). – menteith Sep 15 '19 at 13:29
  • See also [When Runtime.exec() won't](http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html) 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. – Andrew Thompson Sep 15 '19 at 15:07
  • Or nearer at hand https://stackoverflow.com/questions/31776546/why-does-runtime-execstring-work-for-some-but-not-all-commands and maybe https://stackoverflow.com/questions/5928225/how-to-make-pipes-work-with-runtime-exec and https://stackoverflow.com/questions/2088917/java-exec-does-not-return-expected-result-of-pipes-connected-commands – dave_thompson_085 Sep 15 '19 at 20:25

3 Answers3

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();
    }
barti_ddu
  • 10,179
  • 1
  • 45
  • 53
1

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

Ambro-r
  • 919
  • 1
  • 4
  • 14