Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

I'd like to know how I can code a Java program that knows which Windows application is in focus. I can have many windows open but I want to know the one that's being used (like Google Chrome right now as I'm typing this).

I don't need to change anything in the window or application, just need to know its name.

like image 740
Daniel Loureiro Avatar asked Mar 05 '11 20:03

Daniel Loureiro


1 Answers

As the others have already pointer out, there is no portable way to get this on all platforms. But to make things worse: There not even a consistent way on MS Windows. I will provide some code that will solve the problem for different platforms and will point out the limitations. Use at your own risk, the code may provide wrong results or not run at all because of security reasons. If it runs on your machine, it will not mean that it will run equally well on other machines.

The code uses JNA. During my experiments I had problems with different versions of JNA and the JNA platform library. It might be best to compile it yourself, so you have a consistent environment.

Windows

The answer provided by kichik was correct at its time but will not work with Windows 8 in all cases. The problem is, that it will not handle Metro apps correctly. Unfortunately there is currently no stable API to get the name of the currently running Metro app. I have inserted some hints in the code, but it's best to wait until Microsoft will provide you with an API.

On Windows you will also have problems with privileged apps and with the UAC dialog. So you will not always get a correct answer.

public interface Psapi extends StdCallLibrary {     Psapi INSTANCE = (Psapi) Native.loadLibrary("Psapi", Psapi.class);      WinDef.DWORD GetModuleBaseNameW(Pointer hProcess, Pointer hModule, byte[] lpBaseName, int nSize); }     if (Platform.isWindows()) {         final int PROCESS_VM_READ=0x0010;         final int PROCESS_QUERY_INFORMATION=0x0400;         final User32 user32 = User32.INSTANCE;         final Kernel32 kernel32=Kernel32.INSTANCE;         final Psapi psapi = Psapi.INSTANCE;         WinDef.HWND windowHandle=user32.GetForegroundWindow();         IntByReference pid= new IntByReference();         user32.GetWindowThreadProcessId(windowHandle, pid);         WinNT.HANDLE processHandle=kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, true, pid.getValue());          byte[] filename = new byte[512];         Psapi.INSTANCE.GetModuleBaseNameW(processHandle.getPointer(), Pointer.NULL, filename, filename.length);         String name=new String(filename);         System.out.println(name);         if (name.endsWith("wwahost.exe")) { // Metro App             // There is no stable API to get the current Metro app             // But you can guestimate the name form the current directory of the process             // To query this, see:             // http://stackoverflow.com/questions/16110936/read-other-process-current-directory-in-c-sharp         } 

Linux / Unix / X11

With X11 we have three problems:

  1. Because of network transparency, multiple windows from completely different machines might be mixed in the same X11. So neither name nor PID of the process belonging to a window might be make sense on the machine you are querying.
  2. Most windows managers have mutliple desktops. On each desktop there can be a different application in the foreground
  3. Tiling window managers (like XMonad) don't have the concept of a foreground window. They arrange all windows in a way, so each window is in the foreground at the same time.

On X11 it makes more sense to query for the window that currently has the focus.

public interface XLib extends StdCallLibrary {     XLib INSTANCE = (XLib) Native.loadLibrary("XLib", Psapi.class);      int XGetInputFocus(X11.Display display, X11.Window focus_return, Pointer revert_to_return); }  if(Platform.isLinux()) {  // Possibly most of the Unix systems will work here too, e.g. FreeBSD         final X11 x11 = X11.INSTANCE;         final XLib xlib= XLib.INSTANCE;         X11.Display display = x11.XOpenDisplay(null);         X11.Window window=new X11.Window();         xlib.XGetInputFocus(display, window,Pointer.NULL);         X11.XTextProperty name=new X11.XTextProperty();         x11.XGetWMName(display, window, name);         System.out.println(name.toString());     } 

Mac OS X

Mac OS X does not focus on windows but on applications. So it makes sense to ask for the currently active application. Older versions of Mac OS X provide multiple desktops. Newer versions can have multiple fullscreen applications open at the same time. So you might not always get a correct answer.

    if(Platform.isMac()) {         final String script="tell application \"System Events\"\n" +                 "\tname of application processes whose frontmost is tru\n" +                 "end";         ScriptEngine appleScript=new ScriptEngineManager().getEngineByName("AppleScript");         String result=(String)appleScript.eval(script);         System.out.println(result);     } 

Conclusion

When I played around with this code, it worked in the most basic cases. But if you want this code to run reliable, you will have to put in a lot of polish. Decide for yourself if it is worth it.

To make the code complete, here is the import section I used:

    import com.sun.jna.Native;     import com.sun.jna.Platform;     import com.sun.jna.Pointer;     import com.sun.jna.platform.unix.X11;     import com.sun.jna.platform.win32.Kernel32;     import com.sun.jna.platform.win32.User32;     import com.sun.jna.platform.win32.WinDef;     import com.sun.jna.platform.win32.WinNT;     import com.sun.jna.ptr.IntByReference;     import com.sun.jna.win32.StdCallLibrary;      import javax.script.ScriptEngine;     import javax.script.ScriptEngineManager;     import javax.script.ScriptException; 

Of course you will have to rearrange the parts of the code. I used one big class with the interfaces at the beginning a and then the rest in one big main method.

like image 148
stefan.schwetschke Avatar answered Sep 18 '22 15:09

stefan.schwetschke