View Javadoc
1   /*
2    * Copyright 2021-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.driver.windows;
6   
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.List;
10  import java.util.Map;
11  
12  import com.sun.jna.Pointer;
13  import com.sun.jna.platform.DesktopWindow;
14  import com.sun.jna.platform.WindowUtils;
15  import com.sun.jna.platform.win32.User32;
16  import com.sun.jna.platform.win32.WinDef.DWORD;
17  import com.sun.jna.platform.win32.WinDef.HWND;
18  
19  import oshi.annotation.concurrent.ThreadSafe;
20  import oshi.jna.ByRef.CloseableIntByReference;
21  import oshi.software.os.OSDesktopWindow;
22  
23  /**
24   * Utility to query Desktop windows
25   */
26  @ThreadSafe
27  public final class EnumWindows {
28  
29      private static final DWORD GW_HWNDNEXT = new DWORD(2);
30  
31      private EnumWindows() {
32      }
33  
34      /**
35       * Gets windows on the operating system's GUI desktop.
36       *
37       * @param visibleOnly Whether to restrict the list to only windows visible to the user.
38       * @return A list of {@link oshi.software.os.OSDesktopWindow} objects representing the desktop windows.
39       */
40      public static List<OSDesktopWindow> queryDesktopWindows(boolean visibleOnly) {
41          // Get the windows using JNA's implementation
42          List<DesktopWindow> windows = WindowUtils.getAllWindows(true);
43          // Prepare a list to return
44          List<OSDesktopWindow> windowList = new ArrayList<>();
45          // Populate the list
46          Map<HWND, Integer> zOrderMap = new HashMap<>();
47          for (DesktopWindow window : windows) {
48              HWND hWnd = window.getHWND();
49              if (hWnd != null) {
50                  boolean visible = User32.INSTANCE.IsWindowVisible(hWnd);
51                  if (!visibleOnly || visible) {
52                      if (!zOrderMap.containsKey(hWnd)) {
53                          updateWindowZOrderMap(hWnd, zOrderMap);
54                      }
55                      try (CloseableIntByReference pProcessId = new CloseableIntByReference()) {
56                          User32.INSTANCE.GetWindowThreadProcessId(hWnd, pProcessId);
57                          windowList.add(new OSDesktopWindow(Pointer.nativeValue(hWnd.getPointer()), window.getTitle(),
58                                  window.getFilePath(), window.getLocAndSize(), pProcessId.getValue(),
59                                  zOrderMap.get(hWnd), visible));
60                      }
61                  }
62              }
63          }
64          return windowList;
65      }
66  
67      private static void updateWindowZOrderMap(HWND hWnd, Map<HWND, Integer> zOrderMap) {
68          if (hWnd != null) {
69              int zOrder = 1;
70              HWND h = new HWND(hWnd.getPointer());
71              // First is highest, so decrement
72              do {
73                  zOrderMap.put(h, --zOrder);
74              } while ((h = User32.INSTANCE.GetWindow(h, GW_HWNDNEXT)) != null);
75              // now add lowest value to all
76              final int offset = zOrder * -1;
77              zOrderMap.replaceAll((k, v) -> v + offset);
78          }
79      }
80  }